Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> dependencies. May be
* null if we don't care about module dependencies.
* @param canModifyExterns If true, then we can move prototype
* properties that are declared in the externs file.
* @param anchorUnusedVars If true, then we must mark all vars as referenced,
* even if they are never used.
*/
AnalyzePrototypeProperties(AbstractCompiler compiler,
JSModuleGraph moduleGraph, boolean canModifyExterns,
boolean anchorUnusedVars) {
this.compiler = compiler;
this.moduleGraph = moduleGraph;
this.canModifyExterns = canModifyExterns;
this.anchorUnusedVars = anchorUnusedVars;
if (moduleGraph != null) {
firstModule = moduleGraph.getRootModule();
} else {
firstModule = null;
}
globalNode.markReference(null);
externNode.markReference(null);
symbolGraph.createNode(globalNode);
symbolGraph.createNode(externNode);
for (String property : IMPLICITLY_USED_PROPERTIES) {
NameInfo nameInfo = getNameInfoForName(property, PROPERTY);
if (moduleGraph == null) {
symbolGraph.connect(externNode, null, nameInfo);
} else {
for (JSModule module : moduleGraph.getAllModules()) {
symbolGraph.connect(externNode, module, nameInfo);
}
}
}
}
@Override
public void process(Node externRoot, Node root) {
if (!canModifyExterns) {
NodeTraversal.traverse(compiler, externRoot,
new ProcessExternProperties());
}
NodeTraversal.traverse(compiler, root, new ProcessProperties());
FixedPointGraphTraversal<NameInfo, JSModule> t =
FixedPointGraphTraversal.newTraversal(new PropagateReferences());
t.computeFixedPoint(symbolGraph,
Sets.newHashSet(externNode, globalNode));
}
/**
* Returns information on all prototype properties.
*/
public Collection<NameInfo> getAllNameInfo() {
List<NameInfo> result = Lists.newArrayList(propertyNameInfo.values());
result.addAll(varNameInfo.values());
return result;
}
/**
* Gets the name info for the property or variable of a given name,
* and creates a new one if necessary
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> JSCompiler_stubMap[JSCompiler_stubMethod_id].apply(" +
" this, arguments);" +
" };" +
"}" +
"function JSCompiler_unstubMethod(" +
" JSCompiler_unstubMethod_id, JSCompiler_unstubMethod_body) {" +
" return JSCompiler_stubMap[JSCompiler_unstubMethod_id] = " +
" JSCompiler_unstubMethod_body;" +
"}";
/**
* Creates a new pass for moving prototype properties.
* @param compiler The compiler.
* @param idGenerator An id generator for method stubs.
* @param canModifyExterns If true, then we can move prototype
* properties that are declared in the externs file.
*/
CrossModuleMethodMotion(AbstractCompiler compiler, IdGenerator idGenerator,
boolean canModifyExterns) {
this.compiler = compiler;
this.idGenerator = idGenerator;
this.moduleGraph = compiler.getModuleGraph();
this.analyzer = new AnalyzePrototypeProperties(compiler, moduleGraph,
canModifyExterns, false);
}
@Override
public void process(Node externRoot, Node root) {
// If there are < 2 modules, then we will never move anything,
// so we're done.
if (moduleGraph != null && moduleGraph.getModuleCount() > 1) {
analyzer.process(externRoot, root);
moveMethods(analyzer.getAllNameInfo());
}
}
/**
* Move methods deeper in the module graph when possible.
*/
private void moveMethods(Collection<NameInfo> allNameInfo) {
boolean hasStubDeclaration = idGenerator.hasGeneratedAnyIds();
for (NameInfo nameInfo : allNameInfo) {
if (!nameInfo.isReferenced()) {
// The code below can't do anything with unreferenced name
// infos. They should be skipped to avoid NPE since their
// deepestCommonModuleRef is null.
continue;
}
if (nameInfo.readsClosureVariables()) {
continue;
}
JSModule deepestCommonModuleRef = nameInfo.getDeepestCommonModuleRef();
if (deepestCommonModuleRef == null) {
compiler.report(JSError.make(NULL
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2004 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.*;
/**
* Verifies that constants are only assigned a value once.
* e.g. var XX = 5;
* XX = 3; // error!
* XX++; // error!
*
*/
class ConstCheck extends AbstractPostOrderCallback
implements CompilerPass {
static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR =
DiagnosticType.error(
"JSC_CONSTANT_REASSIGNED_VALUE_ERROR",
"constant {0} assigned a value more than once");
private final AbstractCompiler compiler;
private final Set<Scope.Var> initializedConstants;
/**
* Creates an instance.
*/
public ConstCheck(AbstractCompiler compiler) {
this.compiler = compiler;
this.initializedConstants = new HashSet<Scope.Var>();
}
@Override
public void process(Node externs, Node root) {
Preconditions.checkState(compiler.getLifeCycleStage().isNormalized());
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
if (parent != null &&
parent.isVar() &&
n.hasChildren()) {
String name = n.getString();
Scope.Var var =
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.
*
*/
public class TokenStream {
public static boolean isKeyword(String name) {
boolean id = false;
String s = name;
complete: {
String X = null;
int c;
partial: switch (s.length()) {
case 2: c=s.charAt(1);
if (c=='f') {
if (s.charAt(0)=='i') {id=true; break complete;}
} else if (c=='n') {
if (s.charAt(0)=='i') {id=true; break complete;}
} else if (c=='o') {
if (s.charAt(0)=='d') {id=true; break complete;}
}
break partial;
case 3: switch (s.charAt(0)) {
case 'f':
if (s.charAt(2)=='r' && s.charAt(1)=='o') {
id=true; break complete;
} break partial;
case 'i':
if (s.charAt(2)=='t' && s.charAt(1)=='n') {
id=true; break complete;
} break partial;
case 'n':
if (s.charAt(2)=='w' && s.charAt(1)=='e') {
id=true; break complete;
} break partial;
case 't':
if (s.charAt(2)=='y' && s.charAt(1)=='r') {
id=true; break complete;
} break partial;
case 'v':
if (s.charAt(2)=='r' && s.charAt(1)=='a') {
id=true; break complete;
} break partial;
} break partial;
case 4: switch (s.charAt(0)) {
case 'b': X="byte";id=true; break partial;
case 'c': c=s.charAt(3);
if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {
id=true; break complete;} }
else if (c=='r') {
if (s.charAt(2)=='a' && s.charAt(1)=='h') {
id=true; break
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> complete;
}
}
break partial;
case 'e': c=s.charAt(3);
if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {
id=true; break complete;} }
else if (c=='m') {
if (s.charAt(2)=='u' && s.charAt(1)=='n') {
id=true; break complete;} }
break partial;
case 'g': X="goto";id=true; break partial;
case 'l': X="long";id=true; break partial;
case 'n': X="null";id=true; break partial;
case 't': c=s.charAt(3);
if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {
id=true; break complete;} }
else if (c=='s') {
if (s.charAt(2)=='i' && s.charAt(1)=='h') {
id=true; break complete;} }
break partial;
case 'v': X="void";id=true; break partial;
case 'w': X="with";id=true; break partial;
} break partial;
case 5: switch (s.charAt(2)) {
case 'a': X="class";id=true; break partial;
case 'e': X="break";id=true; break partial;
case 'i': X="while";id=true; break partial;
case 'l': X="false";id=true; break partial;
case 'n': c=s.charAt(0);
if (c=='c') { X="const";id=true; }
else if (c=='f') { X="final";id=true; }
break partial;
case 'o': c=s.charAt(0);
if (c=='f') { X="float";id=true; }
else if (c=='s') { X="short";id=true; }
break partial;
case 'p': X="super";id=true; break partial;
case 'r': X="throw";id=true; break partial;
case 't': X
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>="catch";id=true; break partial;
} break partial;
case 6: switch (s.charAt(1)) {
case 'a': X="native";id=true; break partial;
case 'e': c=s.charAt(0);
if (c=='d') { X="delete";id=true; }
else if (c=='r') { X="return";id=true; }
break partial;
case 'h': X="throws";id=true; break partial;
case 'm': X="import";id=true; break partial;
case 'o': X="double";id=true; break partial;
case 't': X="static";id=true; break partial;
case 'u': X="public";id=true; break partial;
case 'w': X="switch";id=true; break partial;
case 'x': X="export";id=true; break partial;
case 'y': X="typeof";id=true; break partial;
} break partial;
case 7: switch (s.charAt(1)) {
case 'a': X="package";id=true; break partial;
case 'e': X="default";id=true; break partial;
case 'i': X="finally";id=true; break partial;
case 'o': X="boolean";id=true; break partial;
case 'r': X="private";id=true; break partial;
case 'x': X="extends";id=true; break partial;
} break partial;
case 8: switch (s.charAt(0)) {
case 'a': X="abstract";id=true; break partial;
case 'c': X="continue";id=true; break partial;
case 'd': X="debugger";id=true; break partial;
case 'f': X="function";id=true; break partial;
case 'v': X="volatile";id=true; break partial;
} break partial;
case 9: c=s.charAt(0);
if (c=='i') { X="interface";id=true; }
else if (c=='p') { X="protected";id=true; }
else if (c=='t') { X="transient";id=true;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> }
break partial;
case 10: c=s.charAt(1);
if (c=='m') { X="implements";id=true; }
else if (c=='n') { X="instanceof";id=true; }
break partial;
case 12: X="synchronized";id=true; break partial;
}
// patial match validate the entire string the one possiblity
if (X!=null && X!=s && !X.equals(s)) return false;
}
return id;
}
public static boolean isJSIdentifier(String s) {
int length = s.length();
if (length == 0 || !Character.isJavaIdentifierStart(s.charAt(0)))
return false;
for (int i=1; i<length; i++) {
char c = s.charAt(i);
if (!Character.isJavaIdentifierPart(c)) {
if (c == '\\') {
if (! ((i + 5) < length)
&& (s.charAt(i + 1) == 'u')
&& 0 <= xDigitToInt(s.charAt(i + 2), 0)
&& 0 <= xDigitToInt(s.charAt(i + 3), 0)
&& 0 <= xDigitToInt(s.charAt(i + 4), 0)
&& 0 <= xDigitToInt(s.charAt(i + 5), 0)) {
return true;
}
}
return false;
}
}
return true;
}
/**
* If character <tt>c</tt> is a hexadecimal digit, return
* <tt>accumulator</tt> * 16 plus corresponding
* number. Otherise return -1.
*/
private static int xDigitToInt(int c, int accumulator) {
check: {
// Use 0..9 < A..Z < a..z
if (c <= '9') {
c -= '0';
if (0 <= c) { break check; }
} else if (c <= 'F') {
if ('A' <= c) {
c -= ('A' - 10);
break check;
}
} else if (c <= 'f') {
if ('a' <=
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> for few warnings.
// This is probably the right trade-off, but will be slow if there
// are lots of warnings in one file.
js = getCode();
} catch (IOException e) {
return null;
}
int pos = 0;
int startLine = 1;
// If we've saved a previous offset and it's for a line less than the
// one we're searching for, then start at that point.
if (lineNumber >= lastLine) {
pos = lastOffset;
startLine = lastLine;
}
for (int n = startLine; n < lineNumber; n++) {
int nextpos = js.indexOf('\n', pos);
if (nextpos == -1) {
return null;
}
pos = nextpos + 1;
}
// Remember this offset for the next search we do.
lastOffset = pos;
lastLine = lineNumber;
if (js.indexOf('\n', pos) == -1) {
// If next new line cannot be found, there are two cases
// 1. pos already reaches the end of file, then null should be returned
// 2. otherwise, return the contents between pos and the end of file.
if (pos >= js.length()) {
return null;
} else {
return js.substring(pos, js.length());
}
} else {
return js.substring(pos, js.indexOf('\n', pos));
}
}
/**
* Get a region around the indicated line number. The exact definition of a
* region is implementation specific, but it must contain the line indicated
* by the line number. A region must not start or end by a carriage return.
*
* @param lineNumber the line number, 1 being the first line of the file.
* @return The line indicated. Returns {@code null} if it does not exist,
* or if there was an IO exception.
*/
public Region getRegion(int lineNumber) {
String js = "";
try {
js = getCode();
} catch (IOException e) {
return null;
}
int pos = 0;
int startLine = Math.max(1,
lineNumber - (SOURCE_EXCERPT_REGION_LENGTH + 1) / 2 + 1
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>);
for (int n = 1; n < startLine; n++) {
int nextpos = js.indexOf('\n', pos);
if (nextpos == -1) {
break;
}
pos = nextpos + 1;
}
int end = pos;
int endLine = startLine;
for (int n = 0; n < SOURCE_EXCERPT_REGION_LENGTH; n++, endLine++) {
end = js.indexOf('\n', end);
if (end == -1) {
break;
}
end++;
}
if (lineNumber >= endLine) {
return null;
}
if (end == -1) {
int last = js.length() - 1;
if (js.charAt(last) == '\n') {
return
new SimpleRegion(startLine, endLine, js.substring(pos, last));
} else {
return new SimpleRegion(startLine, endLine, js.substring(pos));
}
} else {
return new SimpleRegion(startLine, endLine, js.substring(pos, end));
}
}
@Override
public String toString() {
return fileName;
}
public static SourceFile fromFile(String fileName, Charset c) {
return builder().withCharset(c).buildFromFile(fileName);
}
public static SourceFile fromFile(String fileName) {
return builder().buildFromFile(fileName);
}
public static SourceFile fromFile(File file, Charset c) {
return builder().withCharset(c).buildFromFile(file);
}
public static SourceFile fromFile(File file) {
return builder().buildFromFile(file);
}
public static SourceFile fromCode(String fileName, String code) {
return builder().buildFromCode(fileName, code);
}
public static SourceFile fromCode(String fileName,
String originalPath, String code) {
return builder().withOriginalPath(originalPath)
.buildFromCode(fileName, code);
}
public static SourceFile fromInputStream(String fileName, InputStream s)
throws IOException {
return builder().buildFromInputStream(fileName, s);
}
public static SourceFile fromInputStream(String fileName,
String originalPath, InputStream s) throws IOException {
return builder().withOriginalPath(
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>originalPath)
.buildFromInputStream(fileName, s);
}
public static SourceFile fromReader(String fileName, Reader r)
throws IOException {
return builder().buildFromReader(fileName, r);
}
public static SourceFile fromGenerator(String fileName,
Generator generator) {
return builder().buildFromGenerator(fileName, generator);
}
/** Create a new builder for source files. */
public static Builder builder() {
return new Builder();
}
/**
* A builder interface for source files.
*
* Allows users to customize the Charset, and the original path of
* the source file (if it differs from the path on disk).
*/
public static class Builder {
private Charset charset = Charsets.UTF_8;
private String originalPath = null;
public Builder() {}
/** Set the charset to use when reading from an input stream or file. */
public Builder withCharset(Charset charset) {
this.charset = charset;
return this;
}
/** Set the original path to use. */
public Builder withOriginalPath(String originalPath) {
this.originalPath = originalPath;
return this;
}
public SourceFile buildFromFile(String fileName) {
return buildFromFile(new File(fileName));
}
public SourceFile buildFromFile(File file) {
return new OnDisk(file, originalPath, charset);
}
public SourceFile buildFromCode(String fileName, String code) {
return new Preloaded(fileName, originalPath, code);
}
public SourceFile buildFromInputStream(String fileName, InputStream s)
throws IOException {
return buildFromCode(fileName,
CharStreams.toString(new InputStreamReader(s, charset)));
}
public SourceFile buildFromReader(String fileName, Reader r)
throws IOException {
return buildFromCode(fileName, CharStreams.toString(r));
}
public SourceFile buildFromGenerator(String fileName,
Generator generator) {
return new Generated(fileName, originalPath, generator);
}
}
//////////////////////////////////////////////////////////////////////////////
// Implementations
/**
* A source file where the code has been preloaded.
*/
static class Preloaded extends SourceFile {
private static final long serialVersionUID = 1L;
Preloaded(String fileName, String originalPath, String code) {
super(fileName);
super.set
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> {
preferredBreakPosition = len;
}
}
maybeCutLine();
}
/**
* This may start a new line if the current line is longer than the line
* length threshold.
*/
@Override
void maybeCutLine() {
if (lineLength > lineLengthThreshold) {
// Use the preferred position provided it will break the line.
if (preferredBreakPosition > lineStartPosition &&
preferredBreakPosition < lineStartPosition + lineLength) {
int position = preferredBreakPosition;
code.insert(position, '\n');
reportLineCut(lineIndex, position - lineStartPosition);
lineIndex++;
lineLength -= (position - lineStartPosition);
lineStartPosition = position + 1;
} else {
startNewLine();
}
}
}
@Override
void notePreferredLineBreak() {
preferredBreakPosition = code.length();
}
}
static class Builder {
private final Node root;
private boolean prettyPrint = false;
private boolean lineBreak = false;
private boolean outputTypes = false;
private int lineLengthThreshold = DEFAULT_LINE_LENGTH_THRESHOLD;
private SourceMap sourceMap = null;
private SourceMap.DetailLevel sourceMapDetailLevel =
SourceMap.DetailLevel.ALL;
// Specify a charset to use when outputting source code. If null,
// then just output ASCII.
private Charset outputCharset = null;
private boolean tagAsStrict;
/**
* Sets the root node from which to generate the source code.
* @param node The root node.
*/
Builder(Node node) {
root = node;
}
/**
* Sets whether pretty printing should be used.
* @param prettyPrint If true, pretty printing will be used.
*/
Builder setPrettyPrint(boolean prettyPrint) {
this.prettyPrint = prettyPrint;
return this;
}
/**
* Sets whether line breaking should be done automatically.
* @param lineBreak If true, line breaking is done automatically.
*/
Builder setLineBreak(boolean lineBreak) {
this.lineBreak = lineBreak;
return this;
}
/**
* Sets whether to output closure-style type annotations.
* @param outputTypes If true, outputs closure-style type annotations.
*/
Builder setOutputTypes(boolean output
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Types) {
this.outputTypes = outputTypes;
return this;
}
/**
* Sets the line length threshold that will be used to determine
* when to break lines, if line breaking is on.
*
* @param threshold The line length threshold.
*/
Builder setLineLengthThreshold(int threshold) {
this.lineLengthThreshold = threshold;
return this;
}
/**
* Sets the source map to which to write the metadata about
* the generated source code.
*
* @param sourceMap The source map.
*/
Builder setSourceMap(SourceMap sourceMap) {
this.sourceMap = sourceMap;
return this;
}
/**
* @param level The detail level to use.
*/
Builder setSourceMapDetailLevel(SourceMap.DetailLevel level) {
Preconditions.checkState(level != null);
this.sourceMapDetailLevel = level;
return this;
}
/**
* Set the charset to use when determining what characters need to be
* escaped in the output.
*/
Builder setOutputCharset(Charset outCharset) {
this.outputCharset = outCharset;
return this;
}
/**
* Set whether the output should be tags as ECMASCRIPT 5 Strict.
*/
Builder setTagAsStrict(boolean tagAsStrict) {
this.tagAsStrict = tagAsStrict;
return this;
}
/**
* Generates the source code and returns it.
*/
String build() {
if (root == null) {
throw new IllegalStateException(
"Cannot build without root node being specified");
}
Format outputFormat = outputTypes
? Format.TYPED
: prettyPrint
? Format.PRETTY
: Format.COMPACT;
return toSource(root, outputFormat, lineBreak, lineLengthThreshold,
sourceMap, sourceMapDetailLevel, outputCharset, tagAsStrict);
}
}
enum Format {
COMPACT,
PRETTY,
TYPED
}
/**
* Converts a tree to js code
*/
private static String toSource(Node root, Format outputFormat,
boolean lineBreak, int lineLengthThreshold,
SourceMap sourceMap,
SourceMap.DetailLevel sourceMapDetailLevel,
Charset outputCharset,
boolean tagAsStrict) {
Preconditions.checkState
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>(sourceMapDetailLevel != null);
boolean createSourceMap = (sourceMap != null);
MappedCodePrinter mcp =
outputFormat == Format.COMPACT
? new CompactCodePrinter(
lineBreak, lineLengthThreshold,
createSourceMap, sourceMapDetailLevel)
: new PrettyCodePrinter(
lineLengthThreshold, createSourceMap, sourceMapDetailLevel);
CodeGenerator cg =
outputFormat == Format.TYPED
? new TypedCodeGenerator(mcp, outputCharset)
: new CodeGenerator(mcp, outputCharset);
if (tagAsStrict) {
cg.tagAsStrict();
}
cg.add(root);
mcp.endFile();
String code = mcp.getCode();
if (createSourceMap) {
mcp.generateSourceMap(sourceMap);
}
return code;
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>COMMA) {
Node gramps = parent.getParent();
if (gramps.isCall() &&
parent == gramps.getFirstChild()) {
// Semantically, a direct call to eval is different from an indirect
// call to an eval. See Ecma-262 S15.1.2.1. So it's ok for the first
// expression to a comma to be a no-op if it's used to indirect
// an eval.
if (n == parent.getFirstChild() &&
parent.getChildCount() == 2 &&
n.getNext().isName() &&
"eval".equals(n.getNext().getString())) {
return;
}
}
if (n == parent.getLastChild()) {
for (Node an : parent.getAncestors()) {
int ancestorType = an.getType();
if (ancestorType == Token.COMMA)
continue;
if (ancestorType != Token.EXPR_RESULT &&
ancestorType != Token.BLOCK)
return;
else
break;
}
}
} else if (pt != Token.EXPR_RESULT && pt != Token.BLOCK) {
if (pt == Token.FOR && parent.getChildCount() == 4 &&
(n == parent.getFirstChild() ||
n == parent.getFirstChild().getNext().getNext())) {
// Fall through and look for warnings for the 1st and 3rd child
// of a for.
} else {
return; // it might be ok to not have a side-effect
}
}
boolean isSimpleOp = NodeUtil.isSimpleOperatorType(n.getType());
if (isSimpleOp ||
!NodeUtil.mayHaveSideEffects(n, t.getCompiler())) {
if (n.isQualifiedName() && n.getJSDocInfo() != null) {
// This no-op statement was there so that JSDoc information could
// be attached to the name. This check should not complain about it.
return;
} else if (NodeUtil.isExpressionNode(n)) {
// we already reported the problem when we visited the child.
return;
}
String msg = "This code lacks side-effects. Is there a bug?";
if (n.isString()) {
msg = "Is there a missing
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> the $ must be
* upper case.
* $A Constant - doesn't have to be anything in front of the $
* </pre>
*/
@Override
public boolean isConstant(String name) {
if (name.length() <= 1) {
return false;
}
// In compiled code, '$' is often a namespace delimiter. To allow inlining
// of namespaced constants, we strip off any namespaces here.
int pos = name.lastIndexOf('$');
if (pos >= 0) {
name = name.substring(pos + 1);
if (name.length() == 0) {
return false;
}
}
return isConstantKey(name);
}
@Override
public boolean isConstantKey(String name) {
if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) {
return false;
}
// hack way of checking that there aren't any lower-case letters
return name.toUpperCase().equals(name);
}
/**
* {@inheritDoc}
*
* <p>This enforces Google's convention about enum key names. They must match
* the regular expression {@code [A-Z0-9][A-Z0-9_]*}.
*
* <p>Examples:
* <ul>
* <li>A</li>
* <li>213</li>
* <li>FOO_BAR</li>
* </ul>
*/
@Override
public boolean isValidEnumKey(String key) {
return ENUM_KEY_PATTERN.matcher(key).matches();
}
/**
* {@inheritDoc}
*
* <p>In Google code, parameter names beginning with {@code opt_} are
* treated as optional arguments.
*/
@Override
public boolean isOptionalParameter(Node parameter) {
return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX);
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return VAR_ARGS_NAME.equals(parameter.getString());
}
/**
* {@inheritDoc}
*
* <p>In Google code, any global name starting with an underscore is
* considered exported.
*/
@Override
public boolean isExported(String name, boolean local) {
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> return super.isExported(name, local) ||
(!local && name.startsWith("_"));
}
/**
* {@inheritDoc}
*
* <p>In Google code, private names end with an underscore, and exported
* names are never considered private (see {@link #isExported}).
*/
@Override
public boolean isPrivate(String name) {
return name.endsWith("_") && !isExported(name);
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> LinkedDirectedGraphNode(N nodeValue) {
this.value = nodeValue;
}
@Override
public N getValue() {
return value;
}
@Override
public <A extends Annotation> A getAnnotation() {
throw new UnsupportedOperationException(
"Graph initialized with node annotations turned off");
}
@Override
public void setAnnotation(Annotation data) {
throw new UnsupportedOperationException(
"Graph initialized with node annotations turned off");
}
@Override
public String getColor() {
return "white";
}
@Override
public String getId() {
return "LDN" + hashCode();
}
@Override
public String getLabel() {
return value != null ? value.toString() : "null";
}
@Override
public String toString() {
return getLabel();
}
@Override
public List<DiGraphEdge<N, E>> getInEdges() {
return inEdgeList;
}
@Override
public List<DiGraphEdge<N, E>> getOutEdges() {
return outEdgeList;
}
private Iterator<GraphNode<N, E>> neighborIterator() {
return new NeighborIterator();
}
private class NeighborIterator implements Iterator<GraphNode<N, E>> {
private final Iterator<DiGraphEdge<N, E>> in = inEdgeList.iterator();
private final Iterator<DiGraphEdge<N, E>> out = outEdgeList.iterator();
@Override
public boolean hasNext() {
return in.hasNext() || out.hasNext();
}
@Override
public GraphNode<N, E> next() {
boolean isOut = !in.hasNext();
Iterator<DiGraphEdge<N, E>> curIterator = isOut ? out : in;
DiGraphEdge<N, E> s = curIterator.next();
return isOut ? s.getDestination() : s.getSource();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove not supported.");
}
}
}
/**
* A directed graph node with annotations.
*/
static class AnnotatedLinkedDirectedGraphNode<N, E>
extends LinkedDirectedGraphNode<N, E> {
protected Annotation annotation;
/**
* @param nodeValue Node's value.
*/
AnnotatedLinkedDirected
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>ASCRIPT3);
if (config.isIdeMode || config.languageMode != LanguageMode.ECMASCRIPT3) {
// Do our own identifier check for ECMASCRIPT 5
compilerEnv.setReservedKeywordAsIdentifier(true);
compilerEnv.setAllowKeywordAsObjectPropertyName(true);
}
if (config.isIdeMode) {
compilerEnv.setAllowMemberExprAsFunctionName(true);
}
compilerEnv.setIdeMode(config.isIdeMode);
Parser p = new Parser(compilerEnv, errorReporter);
AstRoot astRoot = null;
try {
astRoot = p.parse(sourceString, sourceFile.getName(), 1);
} catch (EvaluatorException e) {
logger.info(
"Error parsing " + sourceFile.getName() + ": " + e.getMessage());
} finally {
Context.exit();
}
Node root = null;
if (astRoot != null) {
root = IRFactory.transformTree(
astRoot, sourceFile, sourceString, config, errorReporter);
root.setIsSyntheticBlock(true);
}
return root;
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSTypeExpression;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Prepare the AST before we do any checks or optimizations on it.
*
* This pass must run. It should bring the AST into a consistent state,
* and add annotations where necessary. It should not make any transformations
* on the tree that would lose source information, since we need that source
* information for checks.
*
* @author johnlenz@google.com (John Lenz)
*/
class PrepareAst implements CompilerPass {
private final AbstractCompiler compiler;
private final boolean checkOnly;
PrepareAst(AbstractCompiler compiler) {
this(compiler, false);
}
PrepareAst(AbstractCompiler compiler, boolean checkOnly) {
this.compiler = compiler;
this.checkOnly = checkOnly;
}
private void reportChange() {
if (checkOnly) {
Preconditions.checkState(false, "normalizeNodeType constraints violated");
}
}
@Override
public void process(Node externs, Node root) {
if (checkOnly) {
normalizeNodeTypes(root);
} else {
// Don't perform "PrepareAnnotations" when doing checks as
// they currently aren't valid during sanity checks. In particular,
// they DIRECT_EVAL shouldn't be
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> applied after inlining has been
// performed.
if (externs != null) {
NodeTraversal.traverse(
compiler, externs, new PrepareAnnotations(compiler));
}
if (root != null) {
NodeTraversal.traverse(
compiler, root, new PrepareAnnotations(compiler));
}
}
}
/**
* Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code.
*/
private void normalizeNodeTypes(Node n) {
normalizeBlocks(n);
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
// This pass is run during the CompilerTestCase validation, so this
// parent pointer check serves as a more general check.
Preconditions.checkState(child.getParent() == n);
normalizeNodeTypes(child);
}
}
/**
* Add blocks to IF, WHILE, DO, etc.
*/
private void normalizeBlocks(Node n) {
if (NodeUtil.isControlStructure(n)
&& !n.isLabel()
&& !n.isSwitch()) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (NodeUtil.isControlStructureCodeBlock(n,c) &&
!c.isBlock()) {
Node newBlock = IR.block().srcref(n);
n.replaceChild(c, newBlock);
if (!c.isEmpty()) {
newBlock.addChildrenToFront(c);
} else {
newBlock.setWasEmptyNode(true);
}
c = newBlock;
reportChange();
}
}
}
}
/**
* Normalize where annotations appear on the AST. Copies
* around existing JSDoc annotations as well as internal annotations.
*/
static class PrepareAnnotations
implements NodeTraversal.Callback {
private final CodingConvention convention;
PrepareAnnotations(AbstractCompiler compiler) {
this.convention = compiler.getCodingConvention();
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isObjectLit()) {
normalizeObjectLiteralAnnotations(n);
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Type.restrictByNotNullOrUndefined();
type = type.restrictByNotNullOrUndefined();
if (!type.canAssignTo(castType) && !castType.canAssignTo(type)) {
registerMismatch(type, castType, report(t.makeError(n, INVALID_CAST,
castType.toString(), type.toString())));
}
}
/**
* Expect that the given variable has not been declared with a type.
*
* @param sourceName The name of the source file we're in.
* @param n The node where warnings should point to.
* @param parent The parent of {@code n}.
* @param var The variable that we're checking.
* @param variableName The name of the variable.
* @param newType The type being applied to the variable. Mostly just here
* for the benefit of the warning.
* @return The variable we end up with. Most of the time, this will just
* be {@code var}, but in some rare cases we will need to declare
* a new var with new source info.
*/
Var expectUndeclaredVariable(String sourceName, CompilerInput input,
Node n, Node parent, Var var, String variableName, JSType newType) {
Var newVar = var;
boolean allowDupe = false;
if (n.isGetProp() ||
NodeUtil.isObjectLitKey(n, parent)) {
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
info = parent.getJSDocInfo();
}
allowDupe =
info != null && info.getSuppressions().contains("duplicate");
}
JSType varType = var.getType();
// Only report duplicate declarations that have types. Other duplicates
// will be reported by the syntactic scope creator later in the
// compilation process.
if (varType != null &&
varType != typeRegistry.getNativeType(UNKNOWN_TYPE) &&
newType != null &&
newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) {
// If there are two typed declarations of the same variable, that
// is an error and the second declaration is ignored, except in the
// case of native types. A null input type means that the declaration
// was made in Typed
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>ScopeCreator#createInitialScope and is a
// native type. We should redeclare it at the new input site.
if (var.input == null) {
Scope s = var.getScope();
s.undeclare(var);
newVar = s.declare(variableName, n, varType, input, false);
n.setJSType(varType);
if (parent.isVar()) {
if (n.getFirstChild() != null) {
n.getFirstChild().setJSType(varType);
}
} else {
Preconditions.checkState(parent.isFunction());
parent.setJSType(varType);
}
} else {
// Always warn about duplicates if the overridden type does not
// match the original type.
//
// If the types match, suppress the warning iff there was a @suppress
// tag, or if the original declaration was a stub.
if (!(allowDupe ||
var.getParentNode().isExprResult()) ||
!newType.equals(varType)) {
report(JSError.make(sourceName, n, DUP_VAR_DECLARATION,
variableName, newType.toString(), var.getInputName(),
String.valueOf(var.nameNode.getLineno()),
varType.toString()));
}
}
}
return newVar;
}
/**
* Expect that all properties on interfaces that this type implements are
* implemented and correctly typed.
*/
void expectAllInterfaceProperties(NodeTraversal t, Node n,
FunctionType type) {
ObjectType instance = type.getInstanceType();
for (ObjectType implemented : type.getAllImplementedInterfaces()) {
if (implemented.getImplicitPrototype() != null) {
for (String prop :
implemented.getImplicitPrototype().getOwnPropertyNames()) {
expectInterfaceProperty(t, n, instance, implemented, prop);
}
}
}
}
/**
* Expect that the peroperty in an interface that this type implements is
* implemented and correctly typed.
*/
private void expectInterfaceProperty(NodeTraversal t, Node n,
ObjectType instance, ObjectType implementedInterface, String prop) {
if (!instance.hasProperty(prop)) {
// Not implemented
String sourceName = n.getSourceFileName();
sourceName = sourceName == null ? "" : sourceName;
registerMismatch
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>_THIS = DiagnosticType.warning(
"JSC_USED_GLOBAL_THIS",
"dangerous use of the global 'this' object");
private final AbstractCompiler compiler;
/**
* If {@code assignLhsChild != null}, then the node being traversed is
* a descendant of the first child of an ASSIGN node. assignLhsChild's
* parent is this ASSIGN node.
*/
private Node assignLhsChild = null;
CheckGlobalThis(AbstractCompiler compiler) {
this.compiler = compiler;
}
/**
* Since this pass reports errors only when a global {@code this} keyword
* is encountered, there is no reason to traverse non global contexts.
*/
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isFunction()) {
// Don't traverse functions that are constructors or have the @this
// or @override annotation.
JSDocInfo jsDoc = getFunctionJsDocInfo(n);
if (jsDoc != null &&
(jsDoc.isConstructor() ||
jsDoc.isInterface() ||
jsDoc.hasThisType() ||
jsDoc.isOverride())) {
return false;
}
// Don't traverse functions unless they would normally
// be able to have a @this annotation associated with them. e.g.,
// var a = function() { }; // or
// function a() {} // or
// a.x = function() {}; // or
// var a = {x: function() {}};
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN ||
// object literal keys
pType == Token.STRING)) {
return false;
}
// Don't traverse functions that are getting lent to a prototype.
Node gramps = parent.getParent();
if (NodeUtil.isObjectLitKey(parent, gramps)) {
JSDocInfo maybeLends = gramps.getJSDocInfo();
if (maybeLends != null &&
maybeLends.getLendsName() != null &&
maybeLends.getLendsName().endsWith(".prototype")) {
return false;
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> DiagnosticType BAD_JSDOC_ANNOTATION =
DiagnosticType.warning("JSC_BAD_JSDOC_ANNOTATION", "Parse error. {0}");
// A map of Rhino messages to their DiagnosticType.
private final Map<Pattern, DiagnosticType> typeMap;
private final AbstractCompiler compiler;
/**
* For each message such as "Not a good use of {0}", replace the place
* holder {0} with a wild card that matches all possible strings.
* Also put the any non-place-holder in quotes for regex matching later.
*/
private Pattern replacePlaceHolders(String s) {
s = Pattern.quote(s);
return Pattern.compile(s.replaceAll("\\{\\d+\\}", "\\\\E.*\\\\Q"));
}
private RhinoErrorReporter(AbstractCompiler compiler) {
this.compiler = compiler;
typeMap = ImmutableMap.of(
// Extra @fileoverview
replacePlaceHolders(
ScriptRuntime.getMessage0("msg.jsdoc.fileoverview.extra")),
EXTRA_FILEOVERVIEW,
// Trailing comma
replacePlaceHolders(
com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime
.getMessage0("msg.extra.trailing.comma")),
TRAILING_COMMA,
// Duplicate parameter
replacePlaceHolders(
com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime
.getMessage0("msg.dup.parms")),
DUPLICATE_PARAM,
// Unknown @annotations.
replacePlaceHolders(ScriptRuntime.getMessage0("msg.bad.jsdoc.tag")),
BAD_JSDOC_ANNOTATION,
// Type annotation errors.
Pattern.compile("^Bad type annotation.*"),
TYPE_PARSE_ERROR);
}
public static com.google.javascript.jscomp.mozilla.rhino.ErrorReporter
forNewRhino(AbstractCompiler compiler) {
return new NewRhinoErrorReporter(compiler);
}
public static ErrorReporter forOldRhino(AbstractCompiler compiler) {
return new OldRhinoErrorReporter(compiler);
}
public void warning(String message, String sourceName, int line,
int lineOffset) {
compiler.report(
makeError(message, sourceName, line, lineOffset, CheckLevel.WARNING
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
public void process(Node externs, Node root) {
if (namespace == null) {
namespace = new GlobalNamespace(compiler, root);
}
overrideDefines(collectDefines(root, namespace));
}
private void overrideDefines(Map<String, DefineInfo> allDefines) {
boolean changed = false;
for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) {
String defineName = def.getKey();
DefineInfo info = def.getValue();
Node inputValue = dominantReplacements.get(defineName);
Node finalValue = inputValue != null ?
inputValue : info.getLastValue();
if (finalValue != info.initialValue) {
info.initialValueParent.replaceChild(
info.initialValue, finalValue.cloneTree());
compiler.addToDebugLog("Overriding @define variable " + defineName);
changed = changed ||
finalValue.getType() != info.initialValue.getType() ||
!finalValue.isEquivalentTo(info.initialValue);
}
}
if (changed) {
compiler.reportCodeChange();
}
Set<String> unusedReplacements = dominantReplacements.keySet();
unusedReplacements.removeAll(allDefines.keySet());
unusedReplacements.removeAll(KNOWN_DEFINES);
for (String unknownDefine : unusedReplacements) {
compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine));
}
}
private static String format(MessageFormat format, Object... params) {
return format.format(params);
}
/**
* Only defines of literal number, string, or boolean are supported.
*/
private boolean isValidDefineType(JSTypeExpression expression) {
JSType type = expression.evaluate(null, compiler.getTypeRegistry());
return !type.isUnknownType() && type.isSubtype(
compiler.getTypeRegistry().getNativeType(
JSTypeNative.NUMBER_STRING_BOOLEAN));
}
/**
* Finds all defines, and creates a {@link DefineInfo} data structure for
* each one.
* @return A map of {@link DefineInfo} structures, keyed by name.
*/
private Map<String, DefineInfo> collectDefines(Node root,
GlobalNamespace namespace) {
// Find all the global names with a @define annotation
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> List<Name> allDefines = Lists.newArrayList();
for (Name name : namespace.getNameIndex().values()) {
Ref decl = name.getDeclaration();
if (name.docInfo != null && name.docInfo.isDefine()) {
// Process defines should not depend on check types being enabled,
// so we look for the JSDoc instead of the inferred type.
if (isValidDefineType(name.docInfo.getType())) {
allDefines.add(name);
} else {
JSError error = JSError.make(
decl.getSourceName(),
decl.node, INVALID_DEFINE_TYPE_ERROR);
compiler.report(error);
}
} else {
for (Ref ref : name.getRefs()) {
if (ref == decl) {
// Declarations were handled above.
continue;
}
Node n = ref.node;
Node parent = ref.node.getParent();
JSDocInfo info = n.getJSDocInfo();
if (info == null &&
parent.isVar() && parent.hasOneChild()) {
info = parent.getJSDocInfo();
}
if (info != null && info.isDefine()) {
allDefines.add(name);
break;
}
}
}
}
CollectDefines pass = new CollectDefines(compiler, allDefines);
NodeTraversal.traverse(compiler, root, pass);
return pass.getAllDefines();
}
/**
* Finds all assignments to @defines, and figures out the last value of
* the @define.
*/
private static final class CollectDefines implements Callback {
private final AbstractCompiler compiler;
private final Map<String, DefineInfo> assignableDefines;
private final Map<String, DefineInfo> allDefines;
private final Map<Node, RefInfo> allRefInfo;
// A hack that allows us to remove ASSIGN/VAR statements when
// we're currently visiting one of the children of the assign.
private Node lvalueToRemoveLater = null;
// A stack tied to the node traversal, to keep track of whether
// we're in a conditional block. If 1 is at the top, assignment to
// a define is allowed. Otherwise, it's not allowed.
private final Deque<Integer> assignAllowed;
CollectDefines(AbstractCompiler compiler, List<
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Name> listOfDefines) {
this.compiler = compiler;
this.allDefines = Maps.newHashMap();
assignableDefines = Maps.newHashMap();
assignAllowed = new ArrayDeque<Integer>();
assignAllowed.push(1);
// Create a map of references to defines keyed by node for easy lookup
allRefInfo = Maps.newHashMap();
for (Name name : listOfDefines) {
Ref decl = name.getDeclaration();
if (decl != null) {
allRefInfo.put(decl.node,
new RefInfo(decl, name));
}
for (Ref ref : name.getRefs()) {
if (ref == decl) {
// Declarations were handled above.
continue;
}
// If there's a TWIN def, only put one of the twins in.
if (ref.getTwin() == null || !ref.getTwin().isSet()) {
allRefInfo.put(ref.node, new RefInfo(ref, name));
}
}
}
}
/**
* Get a map of {@link DefineInfo} structures, keyed by the name of
* the define.
*/
Map<String, DefineInfo> getAllDefines() {
return allDefines;
}
/**
* Keeps track of whether the traversal is in a conditional branch.
* We traverse all nodes of the parse tree.
*/
@Override
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
updateAssignAllowedStack(n, true);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
RefInfo refInfo = allRefInfo.get(n);
if (refInfo != null) {
Ref ref = refInfo.ref;
Name name = refInfo.name;
String fullName = name.getFullName();
switch (ref.type) {
case SET_FROM_GLOBAL:
case SET_FROM_LOCAL:
Node valParent = getValueParent(ref);
Node val = valParent.getLastChild();
if (valParent.isAssign() && name.isSimpleName() &&
name.getDeclaration() == ref) {
// For defines, it's an error if a simple name is assigned
// before it's declared
compiler.report(
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName));
} else if (processDefineAssignment(t, fullName, val, valParent)) {
// remove the assignment so that the variable is still declared,
// but no longer assigned to a value, e.g.,
// DEF_FOO = 5; // becomes "5;"
// We can't remove the ASSIGN/VAR when we're still visiting its
// children, so we'll have to come back later to remove it.
refInfo.name.removeRef(ref);
lvalueToRemoveLater = valParent;
}
break;
default:
if (t.inGlobalScope()) {
// Treat this as a reference to a define in the global scope.
// After this point, the define must not be reassigned,
// or it's an error.
DefineInfo info = assignableDefines.get(fullName);
if (info != null) {
setDefineInfoNotAssignable(info, t);
assignableDefines.remove(fullName);
}
}
break;
}
}
if (!t.inGlobalScope() &&
n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) {
// warn about @define annotations in local scopes
compiler.report(
t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, ""));
}
if (lvalueToRemoveLater == n) {
lvalueToRemoveLater = null;
if (n.isAssign()) {
Node last = n.getLastChild();
n.removeChild(last);
parent.replaceChild(n, last);
} else {
Preconditions.checkState(n.isName());
n.removeChild(n.getFirstChild());
}
compiler.reportCodeChange();
}
if (n.isCall()) {
if (t.inGlobalScope()) {
// If there's a function call in the global scope,
// we just say it's unsafe and freeze all the defines.
//
// NOTE(nicksantos): We could be a lot smarter here. For example,
// ReplaceOverriddenVars keeps a call graph of all functions and
// which functions/variables that they reference, and tries
// to statically determine which functions are "safe" and which
//
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> {
// First declaration of this define.
info = new DefineInfo(value, valueParent);
allDefines.put(name, info);
assignableDefines.put(name, info);
} else if (info.recordAssignment(value)) {
// The define was already initialized, but this is a safe
// re-assignment.
return true;
} else {
// The define was already initialized, and this is an unsafe
// re-assignment.
compiler.report(
t.makeError(valueParent, DEFINE_NOT_ASSIGNABLE_ERROR,
name, info.getReasonWhyNotAssignable()));
}
}
return false;
}
/**
* Gets the parent node of the value for any assignment to a Name.
* For example, in the assignment
* {@code var x = 3;}
* the parent would be the NAME node.
*/
private static Node getValueParent(Ref ref) {
// there are two types of declarations: VARs and ASSIGNs
return ref.node.getParent() != null &&
ref.node.getParent().isVar() ?
ref.node : ref.node.getParent();
}
/**
* Records the fact that because of the current node in the node traversal,
* the define can't ever be assigned again.
*
* @param info Represents the define variable.
* @param t The current traversal.
*/
private void setDefineInfoNotAssignable(DefineInfo info, NodeTraversal t) {
info.setNotAssignable(format(REASON_DEFINE_NOT_ASSIGNABLE,
t.getLineNumber(), t.getSourceName()));
}
/**
* A simple data structure for associating a Ref with the name
* that it references.
*/
private static class RefInfo {
final Ref ref;
final Name name;
RefInfo(Ref ref, Name name) {
this.ref = ref;
this.name = name;
}
}
}
/**
* A simple class for storing information about a define.
* Gathers the initial value, the last assigned value, and whether
* the define can be safely assigned a new value.
*/
private static final class DefineInfo {
public final Node initialValueParent;
public final Node initialValue;
private Node lastValue;
private boolean isAssignable;
private String reasonNot
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>, Node n,
Node parent) {
return include == nodeTypes.contains(n.getType());
}
}
/**
* Creates a node traversal using the specified callback interface.
*/
public NodeTraversal(AbstractCompiler compiler, Callback cb) {
this(compiler, cb, new SyntacticScopeCreator(compiler));
}
/**
* Creates a node traversal using the specified callback interface
* and the scope creator.
*/
public NodeTraversal(AbstractCompiler compiler, Callback cb,
ScopeCreator scopeCreator) {
this.callback = cb;
if (cb instanceof ScopedCallback) {
this.scopeCallback = (ScopedCallback) cb;
}
this.compiler = compiler;
this.inputId = null;
this.sourceName = "";
this.scopeCreator = scopeCreator;
}
private void throwUnexpectedException(Exception unexpectedException) {
// If there's an unexpected exception, try to get the
// line number of the code that caused it.
String message = unexpectedException.getMessage();
// TODO(user): It is possible to get more information if curNode or
// its parent is missing. We still have the scope stack in which it is still
// very useful to find out at least which function caused the exception.
if (inputId != null) {
message =
unexpectedException.getMessage() + "\n" +
formatNodeContext("Node", curNode) +
(curNode == null ?
"" :
formatNodeContext("Parent", curNode.getParent()));
}
compiler.throwInternalError(message, unexpectedException);
}
private String formatNodeContext(String label, Node n) {
if (n == null) {
return " " + label + ": NULL";
}
return " " + label + "(" + n.toString(false, false, false) + "): "
+ formatNodePosition(n);
}
/**
* Traverses a parse tree recursively.
*/
public void traverse(Node root) {
try {
inputId = NodeUtil.getInputId(root);
sourceName = "";
curNode = root;
pushScope(root);
traverseBranch(root, null);
popScope();
} catch (Exception unexpectedException) {
throwUnexpectedException(unexpectedException);
}
}
public void traverseRoots(Node ... roots
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>) {
traverseRoots(Lists.newArrayList(roots));
}
public void traverseRoots(List<Node> roots) {
if (roots.isEmpty()) {
return;
}
try {
Node scopeRoot = roots.get(0).getParent();
Preconditions.checkState(scopeRoot != null);
inputId = NodeUtil.getInputId(scopeRoot);
sourceName = "";
curNode = scopeRoot;
pushScope(scopeRoot);
for (Node root : roots) {
Preconditions.checkState(root.getParent() == scopeRoot);
traverseBranch(root, scopeRoot);
}
popScope();
} catch (Exception unexpectedException) {
throwUnexpectedException(unexpectedException);
}
}
private static final String MISSING_SOURCE = "[source unknown]";
private String formatNodePosition(Node n) {
if (n == null) {
return MISSING_SOURCE + "\n";
}
int lineNumber = n.getLineno();
int columnNumber = n.getCharno();
String src = compiler.getSourceLine(sourceName, lineNumber);
if (src == null) {
src = MISSING_SOURCE;
}
return sourceName + ":" + lineNumber + ":" + columnNumber + "\n"
+ src + "\n";
}
/**
* Traverses a parse tree recursively with a scope, starting with the given
* root. This should only be used in the global scope. Otherwise, use
* {@link #traverseAtScope}.
*/
void traverseWithScope(Node root, Scope s) {
Preconditions.checkState(s.isGlobal());
inputId = null;
sourceName = "";
curNode = root;
pushScope(s);
traverseBranch(root, null);
popScope();
}
/**
* Traverses a parse tree recursively with a scope, starting at that scope's
* root.
*/
void traverseAtScope(Scope s) {
Node n = s.getRootNode();
if (n.isFunction()) {
// We need to do some extra magic to make sure that the scope doesn't
// get re-created when we dive into the function.
if (inputId == null) {
inputId = NodeUtil.getInputId(n);
}
sourceName = getSourceName(n);
cur
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Node = n;
pushScope(s);
Node args = n.getFirstChild().getNext();
Node body = args.getNext();
traverseBranch(args, n);
traverseBranch(body, n);
popScope();
} else {
traverseWithScope(n, s);
}
}
/**
* Traverses an inner node recursively with a refined scope. An inner node may
* be any node with a non {@code null} parent (i.e. all nodes except the
* root).
*
* @param node the node to traverse
* @param parent the node's parent, it may be not be {@code null}
* @param refinedScope the refined scope of the scope currently at the top of
* the scope stack or in trivial cases that very scope or {@code null}
*/
protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) {
Preconditions.checkNotNull(parent);
if (refinedScope != null && getScope() != refinedScope) {
curNode = node;
pushScope(refinedScope);
traverseBranch(node, parent);
popScope();
} else {
traverseBranch(node, parent);
}
}
/**
* Gets the compiler.
*/
public Compiler getCompiler() {
// TODO(nicksantos): Remove this type cast. This is just temporary
// while refactoring.
return (Compiler) compiler;
}
/**
* Gets the current line number, or zero if it cannot be determined. The line
* number is retrieved lazily as a running time optimization.
*/
public int getLineNumber() {
Node cur = curNode;
while (cur != null) {
int line = cur.getLineno();
if (line >=0) {
return line;
}
cur = cur.getParent();
}
return 0;
}
/**
* Gets the current input source name.
*
* @return A string that may be empty, but not null
*/
public String getSourceName() {
return sourceName;
}
/**
* Gets the current input source.
*/
public CompilerInput getInput() {
return compiler.getInput(inputId);
}
/**
* Gets the current input module.
*/
public JSModule getModule()
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> {
CompilerInput input = getInput();
return input == null ? null : input.getModule();
}
/** Returns the node currently being traversed. */
public Node getCurrentNode() {
return curNode;
}
/**
* Traverses a node recursively.
*/
public static void traverse(
AbstractCompiler compiler, Node root, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverse(root);
}
/**
* Traverses a list of node trees.
*/
public static void traverseRoots(
AbstractCompiler compiler, List<Node> roots, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
public static void traverseRoots(
AbstractCompiler compiler, Callback cb, Node ... roots) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
/**
* Traverses a branch.
*/
@SuppressWarnings("fallthrough")
private void traverseBranch(Node n, Node parent) {
int type = n.getType();
if (type == Token.SCRIPT) {
inputId = n.getInputId();
sourceName = getSourceName(n);
}
curNode = n;
if (!callback.shouldTraverse(this, n, parent)) return;
switch (type) {
case Token.FUNCTION:
traverseFunction(n, parent);
break;
default:
for (Node child = n.getFirstChild(); child != null; ) {
// child could be replaced, in which case our child node
// would no longer point to the true next
Node next = child.getNext();
traverseBranch(child, n);
child = next;
}
break;
}
curNode = n;
callback.visit(this, n, parent);
}
/**
* Traverses a function.
*/
private void traverseFunction(Node n, Node parent) {
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.isFunction());
final Node fnName = n.getFirstChild();
boolean isFunctionExpression = (parent != null)
&& NodeUtil.isFunctionExpression(n);
if (!isFunctionExpression) {
// Functions declarations are in the scope containing
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> the declaration.
traverseBranch(fnName, n);
}
curNode = n;
pushScope(n);
if (isFunctionExpression) {
// Function expression names are only accessible within the function
// scope.
traverseBranch(fnName, n);
}
final Node args = fnName.getNext();
final Node body = args.getNext();
// Args
traverseBranch(args, n);
// Body
Preconditions.checkState(body.getNext() == null &&
body.isBlock());
traverseBranch(body, n);
popScope();
}
/** Examines the functions stack for the last instance of a function node. */
@SuppressWarnings("unchecked")
public Node getEnclosingFunction() {
if (scopes.size() + scopeRoots.size() < 2) {
return null;
} else {
if (scopeRoots.isEmpty()) {
return scopes.peek().getRootNode();
} else {
return scopeRoots.peek();
}
}
}
/** Creates a new scope (e.g. when entering a function). */
private void pushScope(Node node) {
Preconditions.checkState(curNode != null);
scopeRoots.push(node);
cfgs.push(null);
if (scopeCallback != null) {
scopeCallback.enterScope(this);
}
}
/** Creates a new scope (e.g. when entering a function). */
private void pushScope(Scope s) {
Preconditions.checkState(curNode != null);
scopes.push(s);
cfgs.push(null);
if (scopeCallback != null) {
scopeCallback.enterScope(this);
}
}
/** Pops back to the previous scope (e.g. when leaving a function). */
private void popScope() {
if (scopeCallback != null) {
scopeCallback.exitScope(this);
}
if (scopeRoots.isEmpty()) {
scopes.pop();
} else {
scopeRoots.pop();
}
cfgs.pop();
}
/** Gets the current scope. */
public Scope getScope() {
Scope scope = scopes.isEmpty() ? null : scopes.peek();
if (scopeRoots.isEmpty()) {
return scope;
}
Iterator<Node> it = scopeRoots.descendingIterator();
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
while (it.hasNext()) {
scope = scopeCreator.createScope(it.next(), scope);
scopes.push(scope);
}
scopeRoots.clear();
return scope;
}
/** Gets the control flow graph for the current JS scope. */
public ControlFlowGraph<Node> getControlFlowGraph() {
if (cfgs.peek() == null) {
ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true);
cfa.process(null, getScopeRoot());
cfgs.pop();
cfgs.push(cfa.getCfg());
}
return cfgs.peek();
}
/** Returns the current scope's root. */
public Node getScopeRoot() {
if (scopeRoots.isEmpty()) {
return scopes.peek().getRootNode();
} else {
return scopeRoots.peek();
}
}
/**
* Determines whether the traversal is currently in the global scope.
*/
boolean inGlobalScope() {
return getScopeDepth() <= 1;
}
int getScopeDepth() {
return scopes.size() + scopeRoots.size();
}
public boolean hasScope() {
return !(scopes.isEmpty() && scopeRoots.isEmpty());
}
/** Reports a diagnostic (error or warning) */
public void report(Node n, DiagnosticType diagnosticType,
String... arguments) {
JSError error = JSError.make(getSourceName(), n, diagnosticType, arguments);
compiler.report(error);
}
private static String getSourceName(Node n) {
String name = n.getSourceFileName();
return name == null ? "" : name;
}
InputId getInputId() {
return inputId;
}
/**
* Creates a JSError during NodeTraversal.
*
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public JSError makeError(Node n, CheckLevel level, DiagnosticType type,
String... arguments) {
return JSError.make(getSourceName(), n, level, type, arguments);
}
/**
* Creates a JSError during NodeTraversal.
*
* @param n Determines the line and char position within the source file name
* @
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>propdef.hasOneChild());
objectlit.addChildToBack(propdef);
}
return objectlit;
}
// TODO(johnlenz): quoted props
public static Node propdef(Node string, Node value) {
Preconditions.checkState(string.isString());
Preconditions.checkState(!string.hasChildren());
Preconditions.checkState(mayBeExpression(value));
string.addChildToFront(value);
return string;
}
public static Node arraylit(Node ... exprs) {
Node arraylit = new Node(Token.ARRAYLIT);
for (Node expr : exprs) {
Preconditions.checkState(mayBeExpressionOrEmpty(expr));
arraylit.addChildToBack(expr);
}
return arraylit;
}
public static Node regexp(Node expr) {
Preconditions.checkState(expr.isString());
return new Node(Token.REGEXP, expr);
}
public static Node regexp(Node expr, Node flags) {
Preconditions.checkState(expr.isString());
Preconditions.checkState(flags.isString());
return new Node(Token.REGEXP, expr, flags);
}
public static Node string(String s) {
return Node.newString(s);
}
public static Node number(double d) {
return Node.newNumber(d);
}
public static Node thisNode() {
return new Node(Token.THIS);
}
public static Node trueNode() {
return new Node(Token.TRUE);
}
public static Node falseNode() {
return new Node(Token.FALSE);
}
public static Node nullNode() {
return new Node(Token.NULL);
}
// helper methods
private static Node binaryOp(int token, Node expr1, Node expr2) {
Preconditions.checkState(mayBeExpression(expr1));
Preconditions.checkState(mayBeExpression(expr2));
return new Node(token, expr1, expr2);
}
private static Node unaryOp(int token, Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(token, expr);
}
private static boolean mayBeExpressionOrEmpty(Node n) {
return n.isEmpty() || mayBeExpression(n);
}
private static boolean isAssignmentTarget(Node n) {
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.length; i++) {
expected[i] = "";
for (CompilerInput input : modules[i].getInputs()) {
expected[i] += input.getSourceFile().getCode();
}
}
test(modules, expected, null, warning);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Verifies that the compiler pass's JS output matches the expected output
* and (optionally) that an expected warning is issued. Or, if an error is
* expected, this method just verifies that the error is encountered.
*
* @param compiler A compiler that has been initialized via
* {@link Compiler#init}
* @param expected Expected output, or null if an error is expected
* @param error Expected error, or null if no error is expected
* @param warning Expected warning, or null if no warning is expected
*/
protected void test(Compiler compiler, String[] expected,
DiagnosticType error, DiagnosticType warning) {
test(compiler, expected, error, warning, null);
}
/**
* Verifies that the compiler pass's JS output matches the expected output
* and (optionally) that an expected warning is issued. Or, if an error is
* expected, this method just verifies that the error is encountered.
*
* @param compiler A compiler that has been initialized via
* {@link Compiler#init}
* @param expected Expected output, or null if an error is expected
* @param error Expected error, or null if no error is expected
* @param warning Expected warning, or null if no warning is expected
* @param description The description of the expected warning,
* or null if no warning is expected or if the warning's description
* should not be examined
*/
private void test(Compiler compiler, String[] expected,
DiagnosticType error, DiagnosticType warning,
String description) {
RecentChange recentChange = new RecentChange();
compiler.addChangeHandler(recentChange);
Node root = compiler.parseInputs();
assertTrue("Unexpected parse error(s): " +
Joiner.on("\n").join(compiler.getErrors()), root != null);
if (astValidationEnabled) {
(new AstValidator()).validateRoot(root);
}
Node externsRoot = root.getFirstChild();
Node mainRoot
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> = root.getLastChild();
// Save the tree for later comparison.
Node rootClone = root.cloneTree();
Node externsRootClone = rootClone.getFirstChild();
Node mainRootClone = rootClone.getLastChild();
int numRepetitions = getNumRepetitions();
ErrorManager[] errorManagers = new ErrorManager[numRepetitions];
int aggregateWarningCount = 0;
List<JSError> aggregateWarnings = Lists.newArrayList();
boolean hasCodeChanged = false;
assertFalse("Code should not change before processing",
recentChange.hasCodeChanged());
for (int i = 0; i < numRepetitions; ++i) {
if (compiler.getErrorCount() == 0) {
errorManagers[i] = new BlackHoleErrorManager(compiler);
// Only run the type checking pass once, if asked.
// Running it twice can cause unpredictable behavior because duplicate
// objects for the same type are created, and the type system
// uses reference equality to compare many types.
if (typeCheckEnabled && i == 0) {
TypeCheck check = createTypeCheck(compiler, typeCheckLevel);
check.processForTesting(externsRoot, mainRoot);
}
// Only run the normalize pass once, if asked.
if (normalizeEnabled && i == 0) {
normalizeActualCode(compiler, externsRoot, mainRoot);
}
if (markNoSideEffects && i == 0) {
MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler);
mark.process(externsRoot, mainRoot);
}
recentChange.reset();
getProcessor(compiler).process(externsRoot, mainRoot);
if (astValidationEnabled) {
(new AstValidator()).validateRoot(root);
}
if (checkLineNumbers) {
(new LineNumberCheck(compiler)).process(externsRoot, mainRoot);
}
hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged();
aggregateWarningCount += errorManagers[i].getWarningCount();
aggregateWarnings.addAll(Lists.newArrayList(compiler.getWarnings()));
if (normalizeEnabled) {
boolean verifyDeclaredConstants = true;
new Normalize.VerifyConstants(compiler, verifyDeclaredConstants)
.process(externsRoot, mainRoot);
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>ternsChanges) {
String explanation = externsRootClone.checkTreeEquals(externsRoot);
fail("Unexpected changes to externs" +
"\nExpected: " + compiler.toSource(externsRootClone) +
"\nResult: " + compiler.toSource(externsRoot) +
"\n" + explanation);
}
if (!codeChange && !externsChange) {
assertFalse(
"compiler.reportCodeChange() was called " +
"even though nothing changed",
hasCodeChanged);
} else {
assertTrue("compiler.reportCodeChange() should have been called",
hasCodeChanged);
}
if (compareAsTree) {
String explanation = expectedRoot.checkTreeEquals(mainRoot);
assertNull("\nExpected: " + compiler.toSource(expectedRoot) +
"\nResult: " + compiler.toSource(mainRoot) +
"\n" + explanation, explanation);
} else if (expected != null) {
assertEquals(
Joiner.on("").join(expected), compiler.toSource(mainRoot));
}
// Verify normalization is not invalidated.
Node normalizeCheckRootClone = root.cloneTree();
Node normalizeCheckExternsRootClone = root.getFirstChild();
Node normalizeCheckMainRootClone = root.getLastChild();
new PrepareAst(compiler).process(
normalizeCheckExternsRootClone, normalizeCheckMainRootClone);
String explanation =
normalizeCheckMainRootClone.checkTreeEquals(mainRoot);
assertNull("Node structure normalization invalidated.\nExpected: " +
compiler.toSource(normalizeCheckMainRootClone) +
"\nResult: " + compiler.toSource(mainRoot) +
"\n" + explanation, explanation);
// TODO(johnlenz): enable this for most test cases.
// Currently, this invalidates test for while-loops, for-loop
// initializers, and other naming. However, a set of code
// (FoldConstants, etc) runs before the Normalize pass, so this can't be
// force on everywhere.
if (normalizeEnabled) {
new Normalize(compiler, true).process(
normalizeCheckExternsRootClone, normalizeCheckMainRootClone);
explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot);
assertNull("Normalization invalidated.\
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>nExpected: " +
compiler.toSource(normalizeCheckMainRootClone) +
"\nResult: " + compiler.toSource(mainRoot) +
"\n" + explanation, explanation);
}
} else {
String errors = "";
for (JSError actualError : compiler.getErrors()) {
errors += actualError.description + "\n";
}
assertEquals("There should be one error. " + errors,
1, compiler.getErrorCount());
assertEquals(errors, error, compiler.getErrors()[0].getType());
if (warning != null) {
String warnings = "";
for (JSError actualError : compiler.getWarnings()) {
warnings += actualError.description + "\n";
}
assertEquals("There should be one warning. " + warnings,
1, compiler.getWarningCount());
assertEquals(warnings, warning, compiler.getWarnings()[0].getType());
}
}
}
private void normalizeActualCode(
Compiler compiler, Node externsRoot, Node mainRoot) {
Normalize normalize = new Normalize(compiler, false);
normalize.process(externsRoot, mainRoot);
}
/**
* Parses expected js inputs and returns the root of the parse tree.
*/
protected Node parseExpectedJs(String[] expected) {
Compiler compiler = createCompiler();
JSSourceFile[] inputs = new JSSourceFile[expected.length];
for (int i = 0; i < expected.length; i++) {
inputs[i] = JSSourceFile.fromCode("expected" + i, expected[i]);
}
compiler.init(externsInputs, inputs, getOptions());
Node root = compiler.parseInputs();
assertTrue("Unexpected parse error(s): " +
Joiner.on("\n").join(compiler.getErrors()), root != null);
Node externsRoot = root.getFirstChild();
Node mainRoot = externsRoot.getNext();
// Only run the normalize pass, if asked.
if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) {
Normalize normalize = new Normalize(compiler, false);
normalize.process(externsRoot, mainRoot);
}
return mainRoot;
}
protected Node parseExpectedJs(String expected) {
return parseExpectedJs(new String[] {expected});
}
/**
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> (int i = 0; i < inputs.length; i++) {
JSModule module = modules[i] = new JSModule("m" + i);
module.add(JSSourceFile.fromCode("i" + i, inputs[i]));
}
return modules;
}
private static class BlackHoleErrorManager extends BasicErrorManager {
private BlackHoleErrorManager(Compiler compiler) {
compiler.setErrorManager(this);
}
@Override
public void println(CheckLevel level, JSError error) {}
@Override
public void printSummary() {}
}
Compiler createCompiler() {
Compiler compiler = new Compiler();
return compiler;
}
protected void setExpectedSymbolTableError(DiagnosticType type) {
this.expectedSymbolTableError = type;
}
/** Finds the first matching qualified name node in post-traversal order. */
protected final Node findQualifiedNameNode(final String name, Node root) {
final List<Node> matches = Lists.newArrayList();
NodeUtil.visitPostOrder(root,
new NodeUtil.Visitor() {
@Override public void visit(Node n) {
if (name.equals(n.getQualifiedName())) {
matches.add(n);
}
}
},
Predicates.<Node>alwaysTrue());
return matches.get(0);
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>public interface StaticScope<T> {
/**
* Returns the root node associated with this scope. May be null.
*/
Node getRootNode();
/** Returns the scope enclosing this one or null if none. */
StaticScope<T> getParentScope();
/**
* Returns any defined slot within this scope for this name. This call
* continues searching through parent scopes if a slot with this name is not
* found in the current scope.
* @param name The name of the variable slot to look up.
* @return The defined slot for the variable, or {@code null} if no
* definition exists.
*/
StaticSlot<T> getSlot(String name);
/** Like {@code getSlot} but does not recurse into parent scopes. */
StaticSlot<T> getOwnSlot(String name);
/** Returns the expected type of {@code this} in the current scope. */
T getTypeOfThis();
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>);
/**
* Gets the central registry of type violations.
*/
abstract TypeValidator getTypeValidator();
/**
* Parses code for injecting.
*/
abstract Node parseSyntheticCode(String code);
/**
* Parses code for injecting, and associate it with a given source file.
*/
abstract Node parseSyntheticCode(String filename, String code);
/**
* Parses code for testing.
*/
abstract Node parseTestCode(String code);
/**
* Prints a node to source code.
*/
abstract String toSource(Node root);
/**
* Gets a default error reporter for injecting into Rhino.
*/
abstract ErrorReporter getDefaultErrorReporter();
/**
* Get an interpreter for type analysis.
*/
public abstract ReverseAbstractInterpreter getReverseAbstractInterpreter();
/**
* @return The current life-cycle stage of the AST we're working on.
*/
LifeCycleStage getLifeCycleStage() {
return stage;
}
/**
* Generates unique ids.
*/
abstract Supplier<String> getUniqueNameIdSupplier();
/**
* @return Whether any errors have been encountered that
* should stop the compilation process.
*/
abstract boolean hasHaltingErrors();
/**
* Register a listener for code change events.
*/
abstract void addChangeHandler(CodeChangeHandler handler);
/**
* Remove a listener for code change events.
*/
abstract void removeChangeHandler(CodeChangeHandler handler);
/**
* Returns true if compiling in IDE mode.
*/
abstract boolean isIdeMode();
/**
* @return Whether the compiler is in ES5Mode.
*/
abstract boolean acceptEcmaScript5();
/**
* @return Whether the compiler accepts `const' keyword.
*/
abstract boolean acceptConstKeyword();
/**
* Returns the parser configuration.
*/
abstract Config getParserConfig();
/**
* Returns true if type checking is enabled.
*/
abstract boolean isTypeCheckingEnabled();
/**
* Normalizes the types of AST nodes in the given tree, and
* annotates any nodes to which the coding convention applies so that passes
* can read the annotations instead of using the coding convention.
*/
abstract void prepareAst(Node root);
/**
* Gets the error manager.
*/
abstract public ErrorManager getErrorManager();
/**
*
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>(
SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3));
}
};
abstract SourceMap getInstance();
}
/**
* Source maps can be very large different levels of detail can be specified.
*/
public static enum DetailLevel implements Predicate<Node> {
// ALL is best when the fullest details are needed for debugging or for
// code-origin analysis.
ALL {
@Override public boolean apply(Node node) {
return true;
}
},
// SYMBOLS is intended to be used for stack trace deobfuscation when full
// detail is not needed.
SYMBOLS {
@Override public boolean apply(Node node) {
return node.isCall()
|| node.isNew()
|| node.isFunction()
|| node.isName()
|| NodeUtil.isGet(node)
|| NodeUtil.isObjectLitKey(node, node.getParent())
|| (node.isString() && NodeUtil.isGet(node.getParent()));
}
};
}
public static class LocationMapping {
final String prefix;
final String replacement;
public LocationMapping(String prefix, String replacement) {
this.prefix = prefix;
this.replacement = replacement;
}
}
private final SourceMapGenerator generator;
private List<LocationMapping> prefixMappings = Collections.emptyList();
private final Map<String, String> sourceLocationFixupCache =
Maps.newHashMap();
private SourceMap(SourceMapGenerator generator) {
this.generator = generator;
}
public void addMapping(
Node node,
FilePosition outputStartPosition,
FilePosition outputEndPosition) {
String sourceFile = node.getSourceFileName();
// If the node does not have an associated source file or
// its line number is -1, then the node does not have sufficient
// information for a mapping to be useful.
if (sourceFile == null || node.getLineno() < 0) {
return;
}
sourceFile = fixupSourceLocation(sourceFile);
String originalName = (String) node.getProp(Node.ORIGINALNAME_PROP);
// Strangely, Rhino source lines are one based but columns are
// zero based.
// We don't change this for the v1 or v2 source maps but for
// v3 we make them
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> both 0 based.
int lineBaseOffset = 1;
if (generator instanceof SourceMapGeneratorV1
|| generator instanceof SourceMapGeneratorV2) {
lineBaseOffset = 0;
}
generator.addMapping(
sourceFile, originalName,
new FilePosition(node.getLineno() - lineBaseOffset, node.getCharno()),
outputStartPosition, outputEndPosition);
}
/**
* @param sourceFile The source file location to fixup.
* @return a remapped source file.
*/
private String fixupSourceLocation(String sourceFile) {
if (prefixMappings.isEmpty()) {
return sourceFile;
}
String fixed = sourceLocationFixupCache.get(sourceFile);
if (fixed != null) {
return fixed;
}
// Replace the first prefix found with its replacement
for (LocationMapping mapping : prefixMappings) {
if (sourceFile.startsWith(mapping.prefix)) {
fixed = mapping.replacement + sourceFile.substring(
mapping.prefix.length());
break;
}
}
// If none of the mappings match then use the original file path.
if (fixed == null) {
fixed = sourceFile;
}
sourceLocationFixupCache.put(sourceFile, fixed);
return fixed;
}
public void appendTo(Appendable out, String name) throws IOException {
generator.appendTo(out, name);
}
public void reset() {
generator.reset();
sourceLocationFixupCache.clear();
}
public void setStartingPosition(int offsetLine, int offsetIndex) {
generator.setStartingPosition(offsetLine, offsetIndex);
}
public void setWrapperPrefix(String prefix) {
generator.setWrapperPrefix(prefix);
}
public void validate(boolean validate) {
generator.validate(validate);
}
/**
* @param sourceMapLocationMappings
*/
public void setPrefixMappings(List<LocationMapping> sourceMapLocationMappings) {
this.prefixMappings = sourceMapLocationMappings;
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
* Lists of modules at each depth. <code>modulesByDepth.get(3)</code> is a
* list of the modules at depth 3, for example.
*/
private List<List<JSModule>> modulesByDepth;
/**
* dependencyMap is a cache of dependencies that makes the dependsOn
* function faster. Each map entry associates a starting
* JSModule with the set of JSModules that are transitively dependent on the
* starting module.
*
* If the cache returns null, then the entry hasn't been filled in for that
* module.
*
* dependencyMap should be filled from leaf to root so that
* getTransitiveDepsDeepestFirst can use its results directly.
*/
private Map<JSModule, Set<JSModule>> dependencyMap = Maps.newHashMap();
/**
* Creates a module graph from a list of modules in dependency order.
*/
public JSModuleGraph(JSModule[] modulesInDepOrder) {
this(Lists.<JSModule>newArrayList(modulesInDepOrder));
}
/**
* Creates a module graph from a list of modules in dependency order.
*/
public JSModuleGraph(List<JSModule> modulesInDepOrder) {
modules = Sets.newHashSetWithExpectedSize(modulesInDepOrder.size());
modulesByDepth = Lists.newArrayList();
for (JSModule module : modulesInDepOrder) {
int depth = 0;
for (JSModule dep : module.getDependencies()) {
int depDepth = dep.getDepth();
if (depDepth < 0) {
throw new ModuleDependenceException(String.format(
"Modules not in dependency order: %s preceded %s",
module.getName(), dep.getName()),
module, dep);
}
depth = Math.max(depth, depDepth + 1);
}
module.setDepth(depth);
modules.add(module);
if (depth == modulesByDepth.size()) {
modulesByDepth.add(new ArrayList<JSModule>());
}
modulesByDepth.get(depth).add(module);
}
}
/**
* Gets an iterable over all modules.
*/
Iterable<JSModule> getAllModules() {
return modules;
}
/**
* Gets all the modules in dependency order. Modules
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> with the same depth
* will be ordered deterministically.
*/
Iterable<JSModule> getAllModulesInDependencyOrder() {
List<JSModule> modules = Lists.newArrayList(getAllModules());
Collections.sort(modules, new DepthComparator());
return modules;
}
/**
* Gets the total number of modules.
*/
int getModuleCount() {
return modules.size();
}
/**
* Gets the root module.
*/
JSModule getRootModule() {
return Iterables.getOnlyElement(modulesByDepth.get(0));
}
/**
* Determines whether this module depends on a given module. Note that a
* module never depends on itself, as that dependency would be cyclic.
*/
public boolean dependsOn(JSModule src, JSModule m) {
Set<JSModule> deps = dependencyMap.get(src);
if (deps == null) {
deps = getTransitiveDepsDeepestFirst(src);
dependencyMap.put(src, deps);
}
return deps.contains(m);
}
/**
* Finds the deepest common dependency of two modules, not including the two
* modules themselves.
*
* @param m1 A module in this graph
* @param m2 A module in this graph
* @return The deepest common dep of {@code m1} and {@code m2}, or null if
* they have no common dependencies
*/
JSModule getDeepestCommonDependency(JSModule m1, JSModule m2) {
int m1Depth = m1.getDepth();
int m2Depth = m2.getDepth();
// According our definition of depth, the result must have a strictly
// smaller depth than either m1 or m2.
for (int depth = Math.min(m1Depth, m2Depth) - 1; depth >= 0; depth--) {
List<JSModule> modulesAtDepth = modulesByDepth.get(depth);
// Look at the modules at this depth in reverse order, so that we use the
// original ordering of the modules to break ties (later meaning deeper).
for (int i = modulesAtDepth.size() - 1; i >= 0; i--) {
JSModule m = modulesAtDepth.get(i);
if
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> String> graphViz =
LinkedDirectedGraph.create();
for (JSModule module : getAllModulesInDependencyOrder()) {
graphViz.createNode(module);
for (JSModule dep : module.getDependencies()) {
graphViz.createNode(dep);
graphViz.connect(module, "->", dep);
}
}
return graphViz;
}
/**
* A module depth comparator that considers a deeper module to be
* "greater than" a shallower module. Uses module names to
* consistently break ties.
*/
private class DepthComparator implements Comparator<JSModule> {
@Override
public int compare(JSModule m1, JSModule m2) {
return depthCompare(m1, m2);
}
}
/**
* A module depth comparator that considers a deeper module to be "less than"
* a shallower module. Uses module names to consistently break ties.
*/
private class InverseDepthComparator implements Comparator<JSModule> {
@Override
public int compare(JSModule m1, JSModule m2) {
return depthCompare(m2, m1);
}
}
private int depthCompare(JSModule m1, JSModule m2) {
if (m1 == m2) {
return 0;
}
int d1 = m1.getDepth();
int d2 = m2.getDepth();
return d1 < d2 ? -1 : d2 == d1 ? m1.getName().compareTo(m2.getName()) : 1;
}
/*
* Exception class for declaring when the modules being fed into a
* JSModuleGraph as input aren't in dependence order, and so can't be
* processed for caching of various dependency-related queries.
*/
protected static class ModuleDependenceException
extends IllegalArgumentException {
private static final long serialVersionUID = 1;
private final JSModule module;
private final JSModule dependentModule;
protected ModuleDependenceException(String message,
JSModule module, JSModule dependentModule) {
super(message);
this.module = module;
this.dependentModule = dependentModule;
}
public JSModule getModule() {
return module;
}
public JSModule getDependentModule() {
return dependentModule;
}
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> {1} because it references {2}");
static final DiagnosticType STRICT_MODULE_DEP_ERROR = DiagnosticType.disabled(
"JSC_STRICT_MODULE_DEPENDENCY",
"module {0} cannot reference {2}, defined in " +
"module {1}");
static final DiagnosticType NAME_REFERENCE_IN_EXTERNS_ERROR =
DiagnosticType.warning(
"JSC_NAME_REFERENCE_IN_EXTERNS",
"accessing name {0} in externs has no effect");
static final DiagnosticType UNDEFINED_EXTERN_VAR_ERROR =
DiagnosticType.warning(
"JSC_UNDEFINED_EXTERN_VAR_ERROR",
"name {0} is not undefined in the externs.");
static final DiagnosticType INVALID_FUNCTION_DECL =
DiagnosticType.error("JSC_INVALID_FUNCTION_DECL",
"Syntax error: function declaration must have a name");
private CompilerInput synthesizedExternsInput = null;
private Node synthesizedExternsRoot = null;
// Vars that still need to be declared in externs. These will be declared
// at the end of the pass, or when we see the equivalent var declared
// in the normal code.
private final Set<String> varsToDeclareInExterns = Sets.newHashSet();
private final AbstractCompiler compiler;
// Whether this is the post-processing sanity check.
private final boolean sanityCheck;
// Whether extern checks emit error.
private final boolean strictExternCheck;
VarCheck(AbstractCompiler compiler) {
this(compiler, false);
}
VarCheck(AbstractCompiler compiler, boolean sanityCheck) {
this.compiler = compiler;
this.strictExternCheck = compiler.getErrorLevel(
JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR;
this.sanityCheck = sanityCheck;
}
@Override
public void process(Node externs, Node root) {
// Don't run externs-checking in sanity check mode. Normalization will
// remove duplicate VAR declarations, which will make
// externs look like they have assigns.
if (!sanityCheck) {
NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck());
}
NodeTraversal.traverseRoots(
compiler
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>, Lists.newArrayList(externs, root), this);
for (String varName : varsToDeclareInExterns) {
createSynthesizedExternVar(varName);
}
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
Preconditions.checkState(scriptRoot.isScript());
NodeTraversal t = new NodeTraversal(compiler, this);
// Note we use the global scope to prevent wrong "undefined-var errors" on
// variables that are defined in other js files.
t.traverseWithScope(scriptRoot,
SyntacticScopeCreator.generateUntypedTopScope(compiler));
// TODO(bashir) Check if we need to createSynthesizedExternVar like process.
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (!n.isName()) {
return;
}
String varName = n.getString();
// Only a function can have an empty name.
if (varName.isEmpty()) {
Preconditions.checkState(parent.isFunction());
// A function declaration with an empty name passes Rhino,
// but is supposed to be a syntax error according to the spec.
if (!NodeUtil.isFunctionExpression(parent)) {
t.report(n, INVALID_FUNCTION_DECL);
}
return;
}
// Check if this is a declaration for a var that has been declared
// elsewhere. If so, mark it as a duplicate.
if ((parent.isVar() ||
NodeUtil.isFunctionDeclaration(parent)) &&
varsToDeclareInExterns.contains(varName)) {
createSynthesizedExternVar(varName);
n.addSuppression("duplicate");
}
// Check that the var has been declared.
Scope scope = t.getScope();
Scope.Var var = scope.getVar(varName);
if (var == null) {
if (NodeUtil.isFunctionExpression(parent)) {
// e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the
// current scope.
} else {
// The extern checks are stricter, don't report a second error.
if (!strictExternCheck || !t.getInput().isExtern()) {
t.report(n, UNDEFINED_VAR_ERROR, var
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> module name (primarily for debugging). */
@Override
public String toString() {
return name;
}
/**
* Removes any references to nodes of the AST. This method is needed to
* allow the ASTs to be garbage collected if the modules are kept around.
*/
public void clearAsts() {
for (CompilerInput input : inputs) {
input.clearAst();
}
}
/**
* Puts the JS files into a topologically sorted order by their dependencies.
*/
public void sortInputsByDeps(Compiler compiler) {
// Set the compiler, so that we can parse requires/provides and report
// errors properly.
for (CompilerInput input : inputs) {
input.setCompiler(compiler);
}
// Sort the JSModule in this order.
try {
List<CompilerInput> sortedList =
(new SortedDependencies<CompilerInput>(
Collections.<CompilerInput>unmodifiableList(inputs)))
.getSortedList();
inputs.clear();
inputs.addAll(sortedList);
} catch (CircularDependencyException e) {
compiler.report(
JSError.make(CIRCULAR_DEPENDENCY_ERROR, e.getMessage()));
}
}
/**
* Returns the given collection of modules in topological order.
*
* Note that this will return the modules in the same order if they are
* already sorted, and in general, will only change the order as necessary to
* satisfy the ordering dependencies. This can be important for cases where
* the modules do not properly specify all dependencies.
*/
public static JSModule[] sortJsModules(Collection<JSModule> modules)
throws CircularDependencyException {
// Sort the JSModule in this order.
List<JSModule> sortedList = (new SortedDependencies<JSModule>(
Lists.newArrayList(modules))).getSortedList();
return sortedList.toArray(new JSModule[sortedList.size()]);
}
/**
* @param dep the depth to set
*/
public void setDepth(int dep) {
this.depth = dep;
}
/**
* @return the depth
*/
public int getDepth() {
return depth;
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>process(Node, Node)}.
*
* @author kushal@google.com (Kushal Dave)
*/
class ReferenceCollectingCallback implements ScopedCallback,
HotSwapCompilerPass,
StaticSymbolTable<Var, ReferenceCollectingCallback.Reference> {
/**
* Maps a given variable to a collection of references to that name. Note that
* Var objects are not stable across multiple traversals (unlike scope root or
* name).
*/
private final Map<Var, ReferenceCollection> referenceMap =
Maps.newHashMap();
/**
* The stack of basic blocks and scopes the current traversal is in.
*/
private final Deque<BasicBlock> blockStack = new ArrayDeque<BasicBlock>();
/**
* Source of behavior at various points in the traversal.
*/
private final Behavior behavior;
/**
* Javascript compiler to use in traversing.
*/
private final AbstractCompiler compiler;
/**
* Only collect references for filtered variables.
*/
private final Predicate<Var> varFilter;
/**
* Constructor initializes block stack.
*/
ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) {
this(compiler, behavior, Predicates.<Var>alwaysTrue());
}
/**
* Constructor only collects references that match the given variable.
*
* The test for Var equality uses reference equality, so it's necessary to
* inject a scope when you traverse.
*/
ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior,
Predicate<Var> varFilter) {
this.compiler = compiler;
this.behavior = behavior;
this.varFilter = varFilter;
}
/**
* Convenience method for running this pass over a tree with this
* class as a callback.
*/
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
}
/**
* Same as process but only runs on a part of AST associated to one script.
*/
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
NodeTraversal.traverse(compiler, scriptRoot, this);
}
/**
* Gets the variables that were referenced in this callback.
*/
@Override
public Iterable<Var> getAllSymbols() {
return referenceMap
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.keySet();
}
@Override
public Scope getScope(Var var) {
return var.scope;
}
/**
* Gets the reference collection for the given variable.
*/
@Override
public ReferenceCollection getReferences(Var v) {
return referenceMap.get(v);
}
/**
* For each node, update the block stack and reference collection
* as appropriate.
*/
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
Var v;
if (n.getString().equals("arguments")) {
v = t.getScope().getArgumentsVar();
} else {
v = t.getScope().getVar(n.getString());
}
if (v != null && varFilter.apply(v)) {
addReference(t, v, new Reference(n, t, blockStack.peek()));
}
}
if (isBlockBoundary(n, parent)) {
blockStack.pop();
}
}
/**
* Updates block stack and invokes any additional behavior.
*/
@Override
public void enterScope(NodeTraversal t) {
Node n = t.getScope().getRootNode();
BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek();
blockStack.push(new BasicBlock(parent, n));
}
/**
* Updates block statck and invokes any additional behavior.
*/
@Override
public void exitScope(NodeTraversal t) {
blockStack.pop();
if (t.getScope().isGlobal()) {
// Update global scope reference lists when we are done with it.
compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot());
behavior.afterExitScope(t, compiler.getGlobalVarReferences());
} else {
behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap));
}
}
/**
* Updates block stack.
*/
@Override
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
// If node is a new basic block, put on basic block stack
if (isBlockBoundary(n, parent)) {
blockStack.push(new BasicBlock(blockStack.peek(), n));
}
return true;
}
/**
* @
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>return true if this node marks the start of a new basic block
*/
private static boolean isBlockBoundary(Node n, Node parent) {
if (parent != null) {
switch (parent.getType()) {
case Token.DO:
case Token.FOR:
case Token.TRY:
case Token.WHILE:
case Token.WITH:
// NOTE: TRY has up to 3 child blocks:
// TRY
// BLOCK
// BLOCK
// CATCH
// BLOCK
// Note that there is an explcit CATCH token but no explicit
// FINALLY token. For simplicity, we consider each BLOCK
// a separate basic BLOCK.
return true;
case Token.AND:
case Token.HOOK:
case Token.IF:
case Token.OR:
// The first child of a conditional is not a boundary,
// but all the rest of the children are.
return n != parent.getFirstChild();
}
}
return n.isCase();
}
private void addReference(NodeTraversal t, Var v, Reference reference) {
// Create collection if none already
ReferenceCollection referenceInfo = referenceMap.get(v);
if (referenceInfo == null) {
referenceInfo = new ReferenceCollection();
referenceMap.put(v, referenceInfo);
}
// Add this particular reference
referenceInfo.add(reference, t, v);
}
interface ReferenceMap {
ReferenceCollection getReferences(Var var);
}
private static class ReferenceMapWrapper implements ReferenceMap {
private final Map<Var, ReferenceCollection> referenceMap;
public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) {
this.referenceMap = referenceMap;
}
@Override
public ReferenceCollection getReferences(Var var) {
return referenceMap.get(var);
}
}
/**
* Way for callers to add specific behavior during traversal that
* utilizes the built-up reference information.
*/
interface Behavior {
/**
* Called after we finish with a scope.
*/
void afterExitScope(NodeTraversal t, ReferenceMap referenceMap);
}
static Behavior DO_NOTHING_BEHAVIOR = new Behavior() {
@Override
public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {}
};
/**
* A collection of
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> references. Can be subclassed to apply checks or
* store additional state when adding.
*/
static class ReferenceCollection implements Iterable<Reference> {
List<Reference> references = Lists.newArrayList();
@Override
public Iterator<Reference> iterator() {
return references.iterator();
}
void add(Reference reference, NodeTraversal t, Var v) {
references.add(reference);
}
/**
* Determines if the variable for this reference collection is
* "well-defined." A variable is well-defined if we can prove at
* compile-time that it's assigned a value before it's used.
*
* Notice that if this function returns false, this doesn't imply that the
* variable is used before it's assigned. It just means that we don't
* have enough information to make a definitive judgement.
*/
protected boolean isWellDefined() {
int size = references.size();
if (size == 0) {
return false;
}
// If this is a declaration that does not instantiate the variable,
// it's not well-defined.
Reference init = getInitializingReference();
if (init == null) {
return false;
}
Preconditions.checkState(references.get(0).isDeclaration());
BasicBlock initBlock = init.getBasicBlock();
for (int i = 1; i < size; i++) {
if (!initBlock.provablyExecutesBefore(
references.get(i).getBasicBlock())) {
return false;
}
}
return true;
}
/**
* Whether the variable is escaped into an inner scope.
*/
boolean isEscaped() {
Scope scope = null;
for (Reference ref : references) {
if (scope == null) {
scope = ref.scope;
} else if (scope != ref.scope) {
return true;
}
}
return false;
}
/**
* @param index The index into the references array to look for an
* assigning declaration.
*
* This is either the declaration if a value is assigned (such as
* "var a = 2", "function a()...", "... catch (a)...").
*/
private boolean isInitializingDeclarationAt(int index) {
Reference maybeInit = references.get(index);
if (maybeInit
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.isInitializingDeclaration()) {
// This is a declaration that represents the initial value.
// Specifically, var declarations without assignments such as "var a;"
// are not.
return true;
}
return false;
}
/**
* @param index The index into the references array to look for an
* initialized assignment reference. That is, an assignment immediately
* follow a variable declaration that itself does not initialize the
* variable.
*/
private boolean isInitializingAssignmentAt(int index) {
if (index < references.size() && index > 0) {
Reference maybeDecl = references.get(index-1);
if (maybeDecl.isVarDeclaration()) {
Preconditions.checkState(!maybeDecl.isInitializingDeclaration());
Reference maybeInit = references.get(index);
if (maybeInit.isSimpleAssignmentToName()) {
return true;
}
}
}
return false;
}
/**
* @return The reference that provides the value for the variable at the
* time of the first read, if known, otherwise null.
*
* This is either the variable declaration ("var a = ...") or first
* reference following the declaration if it is an assignment.
*/
Reference getInitializingReference() {
if (isInitializingDeclarationAt(0)) {
return references.get(0);
} else if (isInitializingAssignmentAt(1)) {
return references.get(1);
}
return null;
}
/**
* Constants are allowed to be defined after their first use.
*/
Reference getInitializingReferenceForConstants() {
int size = references.size();
for (int i = 0; i < size; i++) {
if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) {
return references.get(i);
}
}
return null;
}
/**
* @return Whether the variable is only assigned a value once for its
* lifetime.
*/
boolean isAssignedOnceInLifetime() {
Reference ref = getOneAndOnlyAssignment();
if (ref == null) {
return false;
}
// Make sure this assignment is not in a loop.
for (BasicBlock block = ref.getBasicBlock();
block != null; block = block.getParent()) {
if (block.isFunction) {
break
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>;
} else if (block.isLoop) {
return false;
}
}
return true;
}
/**
* @return The one and only assignment. Returns if there are 0 or 2+
* assignments.
*/
private Reference getOneAndOnlyAssignment() {
Reference assignment = null;
int size = references.size();
for (int i = 0; i < size; i++) {
Reference ref = references.get(i);
if (ref.isLvalue() || ref.isInitializingDeclaration()) {
if (assignment == null) {
assignment = ref;
} else {
return null;
}
}
}
return assignment;
}
/**
* @return Whether the variable is never assigned a value.
*/
boolean isNeverAssigned() {
int size = references.size();
for (int i = 0; i < size; i++) {
Reference ref = references.get(i);
if (ref.isLvalue() || ref.isInitializingDeclaration()) {
return false;
}
}
return true;
}
boolean firstReferenceIsAssigningDeclaration() {
int size = references.size();
if (size > 0 && references.get(0).isInitializingDeclaration()) {
return true;
}
return false;
}
}
/**
* Represents a single declaration or reference to a variable.
*/
static final class Reference implements StaticReference<JSType> {
private static final Set<Integer> DECLARATION_PARENTS =
ImmutableSet.of(Token.VAR, Token.FUNCTION, Token.CATCH);
private final Node nameNode;
private final BasicBlock basicBlock;
private final Scope scope;
private final InputId inputId;
private final StaticSourceFile sourceFile;
Reference(Node nameNode, NodeTraversal t,
BasicBlock basicBlock) {
this(nameNode, basicBlock, t.getScope(), t.getInput().getInputId());
}
// Bleeding functions are weird, because the declaration does
// not appear inside their scope. So they need their own constructor.
static Reference newBleedingFunction(NodeTraversal t,
BasicBlock basicBlock, Node func) {
return new Reference(func.getFirstChild(),
basicBlock, t.getScope(), t.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
BasicBlock getBasicBlock() {
return basicBlock;
}
Node getParent() {
return getNode().getParent();
}
Node getGrandparent() {
Node parent = getParent();
return parent == null ? null : parent.getParent();
}
private static boolean isLhsOfForInExpression(Node n) {
Node parent = n.getParent();
if (parent.isVar()) {
return isLhsOfForInExpression(parent);
}
return NodeUtil.isForIn(parent) && parent.getFirstChild() == n;
}
boolean isSimpleAssignmentToName() {
Node parent = getParent();
return parent.isAssign()
&& parent.getFirstChild() == nameNode;
}
boolean isLvalue() {
Node parent = getParent();
int parentType = parent.getType();
return (parentType == Token.VAR && nameNode.getFirstChild() != null)
|| parentType == Token.INC
|| parentType == Token.DEC
|| (NodeUtil.isAssignmentOp(parent)
&& parent.getFirstChild() == nameNode)
|| isLhsOfForInExpression(nameNode);
}
Scope getScope() {
return scope;
}
}
/**
* Represents a section of code that is uninterrupted by control structures
* (conditional or iterative logic).
*/
static final class BasicBlock {
private final BasicBlock parent;
/**
* Determines whether the block may not be part of the normal control flow,
* but instead "hoisted" to the top of the scope.
*/
private final boolean isHoisted;
/**
* Whether this block denotes a function scope.
*/
private final boolean isFunction;
/**
* Whether this block denotes a loop.
*/
private final boolean isLoop;
/**
* Creates a new block.
* @param parent The containing block.
* @param root The root node of the block.
*/
BasicBlock(BasicBlock parent, Node root) {
this.parent = parent;
// only named functions may be hoisted.
this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root);
this.isFunction = root.isFunction();
if (root.getParent() != null) {
int pType = root.getParent().getType();
this.isLoop
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> the node is not typeable.
boolean typeable = true;
switch (n.getType()) {
case Token.NAME:
typeable = visitName(t, n, parent);
break;
case Token.PARAM_LIST:
// If this is under a FUNCTION node, it is a parameter list and can be
// ignored here.
if (!parent.isFunction()) {
ensureTyped(t, n, getJSType(n.getFirstChild()));
} else {
typeable = false;
}
break;
case Token.COMMA:
ensureTyped(t, n, getJSType(n.getLastChild()));
break;
case Token.TRUE:
case Token.FALSE:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.THIS:
ensureTyped(t, n, t.getScope().getTypeOfThis());
break;
case Token.NULL:
ensureTyped(t, n, NULL_TYPE);
break;
case Token.NUMBER:
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.STRING:
// Object literal keys are handled with OBJECTLIT
if (!NodeUtil.isObjectLitKey(n, n.getParent())) {
ensureTyped(t, n, STRING_TYPE);
} else {
// Object literal keys are not typeable
typeable = false;
}
break;
case Token.GETTER_DEF:
case Token.SETTER_DEF:
// Object literal keys are handled with OBJECTLIT
break;
case Token.ARRAYLIT:
ensureTyped(t, n, ARRAY_TYPE);
break;
case Token.REGEXP:
ensureTyped(t, n, REGEXP_TYPE);
break;
case Token.GETPROP:
visitGetProp(t, n, parent);
typeable = !(parent.isAssign() &&
parent.getFirstChild() == n);
break;
case Token.GETELEM:
visitGetElem(t, n);
// The type of GETELEM is always unknown, so no point counting that.
// If that unknown leaks elsewhere (say by an assignment to another
// variable), then it will be counted.
typeable = false;
break;
case Token.VAR:
visitVar(t, n);
typeable = false;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>(FunctionType ctor) {
Preconditions.checkArgument(ctor.isConstructor() || ctor.isInterface());
Preconditions.checkArgument(!ctor.isUnknownType());
// The type system should notice inheritance cycles on its own
// and break the cycle.
while (true) {
ObjectType maybeSuperInstanceType =
ctor.getPrototype().getImplicitPrototype();
if (maybeSuperInstanceType == null) {
return false;
}
if (maybeSuperInstanceType.isUnknownType() ||
maybeSuperInstanceType.isEmptyType()) {
return true;
}
ctor = maybeSuperInstanceType.getConstructor();
if (ctor == null) {
return false;
}
Preconditions.checkState(ctor.isConstructor() || ctor.isInterface());
}
}
/**
* Visits an ASSIGN node for cases such as
* <pre>
* interface.property2.property = ...;
* </pre>
*/
private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object,
String property, Node lvalue, Node rvalue) {
JSType rvalueType = getJSType(rvalue);
// Only 2 values are allowed for methods:
// goog.abstractMethod
// function () {};
// or for properties, no assignment such as:
// InterfaceFoo.prototype.foobar;
String abstractMethodName =
compiler.getCodingConvention().getAbstractMethodName();
if (!rvalueType.isOrdinaryFunction() &&
!(rvalue.isQualifiedName() &&
rvalue.getQualifiedName().equals(abstractMethodName))) {
// This is bad i18n style but we don't localize our compiler errors.
String abstractMethodMessage = (abstractMethodName != null)
? ", or " + abstractMethodName
: "";
compiler.report(
t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION,
abstractMethodMessage));
}
if (assign.getLastChild().isFunction()
&& !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) {
compiler.report(
t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY,
abstractMethodName));
}
}
/**
* Visits an ASSIGN node for cases such as
* <pre>
* object.property = ...;
* </pre>
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.jscomp.regex.RegExpTree;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.Node;
/**
* Look for references to the global RegExp object that would cause
* regular expressions to be unoptimizable, and checks that regular expressions
* are syntactically valid.
*
* @author johnlenz@google.com (John Lenz)
*/
class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass {
static final DiagnosticType REGEXP_REFERENCE =
DiagnosticType.warning("JSC_REGEXP_REFERENCE",
"References to the global RegExp object prevents " +
"optimization of regular expressions.");
static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning(
"JSC_MALFORMED_REGEXP",
"Malformed Regular Expression: {0}");
private final AbstractCompiler compiler;
private boolean globalRegExpPropertiesUsed = false;
public boolean isGlobalRegExpPropertiesUsed() {
return globalRegExpPropertiesUsed;
}
public CheckRegExp(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (NodeUtil.isReferenceName(n)) {
String name = n.getString();
if (name.equals("RegExp") && t
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> =
new JsDocInfoParser(
new JsDocTokenStream(comment.substring(numOpeningChars),
lineno,
position2charno(position) + numOpeningChars),
node,
irNode,
config,
errorReporter);
jsdocParser.setFileLevelJsDocBuilder(fileLevelJsDocBuilder);
jsdocParser.setFileOverviewJSDocInfo(fileOverviewInfo);
jsdocParser.parse();
return jsdocParser;
}
// Set the length on the node if we're in IDE mode.
private void maybeSetLengthFrom(Node node, AstNode source) {
if (config.isIdeMode) {
node.setLength(source.getLength());
}
}
private int position2charno(int position) {
int lineIndex = sourceString.lastIndexOf('\n', position);
if (lineIndex == -1) {
return position;
} else {
// Subtract one for initial position being 0.
return position - lineIndex - 1;
}
}
private Node justTransform(AstNode node) {
return transformDispatcher.process(node);
}
private class TransformDispatcher extends TypeSafeDispatcher<Node> {
private Node processGeneric(
com.google.javascript.jscomp.mozilla.rhino.Node n) {
Node node = newNode(transformTokenType(n.getType()));
for (com.google.javascript.jscomp.mozilla.rhino.Node child : n) {
node.addChildToBack(transform((AstNode)child));
}
return node;
}
/**
* Transforms the given node and then sets its type to Token.STRING if it
* was Token.NAME. If its type was already Token.STRING, then quotes it.
* Used for properties, as the old AST uses String tokens, while the new one
* uses Name tokens for unquoted strings. For example, in
* var o = {'a' : 1, b: 2};
* the string 'a' is quoted, while the name b is turned into a string, but
* unquoted.
*/
private Node transformAsString(AstNode n) {
Node ret;
if (n instanceof Name) {
ret = transformNameAsString((Name)n);
} else if (n instanceof NumberLiteral) {
ret
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> string is
// \v encoded, then all the vertical tabs in that string
// will be encoded with a \v.
int start = literalNode.getAbsolutePosition();
int end = start + literalNode.getLength();
if (start < sourceString.length() &&
(sourceString.substring(
start, Math.min(sourceString.length(), end))
.indexOf("\\v") != -1)) {
n.putBooleanProp(Node.SLASH_V, true);
}
}
return n;
}
@Override
Node processSwitchCase(SwitchCase caseNode) {
Node node;
if (caseNode.isDefault()) {
node = newNode(Token.DEFAULT_CASE);
} else {
AstNode expr = caseNode.getExpression();
node = newNode(Token.CASE, transform(expr));
}
Node block = newNode(Token.BLOCK);
block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true);
block.setLineno(caseNode.getLineno());
block.setCharno(position2charno(caseNode.getAbsolutePosition()));
maybeSetLengthFrom(block, caseNode);
if (caseNode.getStatements() != null) {
for (AstNode child : caseNode.getStatements()) {
block.addChildToBack(transform(child));
}
}
node.addChildToBack(block);
return node;
}
@Override
Node processSwitchStatement(SwitchStatement statementNode) {
Node node = newNode(Token.SWITCH,
transform(statementNode.getExpression()));
for (AstNode child : statementNode.getCases()) {
node.addChildToBack(transform(child));
}
return node;
}
@Override
Node processThrowStatement(ThrowStatement statementNode) {
return newNode(Token.THROW,
transform(statementNode.getExpression()));
}
@Override
Node processTryStatement(TryStatement statementNode) {
Node node = newNode(Token.TRY,
transformBlock(statementNode.getTryBlock()));
Node block = newNode(Token.BLOCK);
boolean lineSet = false;
for (CatchClause cc : statementNode.getCatchClauses()) {
// Mark the enclosing block at the same line as the first catch
// clause.
if
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
// max length of the number display
int numberLength = Integer.toString(region.getEndingLineNumber())
.length();
// formatting
StringBuilder builder = new StringBuilder(code.length() * 2);
int start = 0;
int end = code.indexOf('\n', start);
int lineNumber = region.getBeginningLineNumber();
while (start >= 0) {
// line extraction
String line;
if (end < 0) {
line = code.substring(start);
if (line.length() == 0) {
return builder.substring(0, builder.length() - 1);
}
} else {
line = code.substring(start, end);
}
builder.append(" ");
// nice spaces for the line number
int spaces = numberLength - Integer.toString(lineNumber).length();
builder.append(Strings.repeat(" ", spaces));
builder.append(lineNumber);
builder.append("| ");
// end & update
if (end < 0) {
builder.append(line);
start = -1;
} else {
builder.append(line);
builder.append('\n');
start = end + 1;
end = code.indexOf('\n', start);
lineNumber++;
}
}
return builder.toString();
}
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> a named function: {0}.");
private final AbstractCompiler compiler;
private final CheckLevel checkLevel;
// NOTE(nicksantos): It's a lot faster to use a shared Set that
// we clear after each method call, because the Set never gets too big.
private final Set<BasicBlock> blocksWithDeclarations = Sets.newHashSet();
public VariableReferenceCheck(AbstractCompiler compiler,
CheckLevel checkLevel) {
this.compiler = compiler;
this.checkLevel = checkLevel;
}
@Override
public void process(Node externs, Node root) {
ReferenceCollectingCallback callback = new ReferenceCollectingCallback(
compiler, new ReferenceCheckingBehavior());
callback.process(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
ReferenceCollectingCallback callback = new ReferenceCollectingCallback(
compiler, new ReferenceCheckingBehavior());
callback.hotSwapScript(scriptRoot, originalRoot);
}
/**
* Behavior that checks variables for redeclaration or early references
* just after they go out of scope.
*/
private class ReferenceCheckingBehavior implements Behavior {
@Override
public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {
// TODO(bashir) In hot-swap version this means that for global scope we
// only go through all global variables accessed in the modified file not
// all global variables. This should be fixed.
// Check all vars after finishing a scope
for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) {
Var v = it.next();
checkVar(t, v, referenceMap.getReferences(v).references);
}
}
/**
* If the variable is declared more than once in a basic block, generate a
* warning. Also check if a variable is used in a given scope before it is
* declared, which suggest a likely error. Relies on the fact that
* references is in parse-tree order.
*/
private void checkVar(NodeTraversal t, Var v, List<Reference> references) {
blocksWithDeclarations.clear();
boolean isDeclaredInScope = false;
boolean isUnhoistedNamedFunction = false;
Reference hoistedFn = null;
// Look for hoisted functions.
for (Reference
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> reference : references) {
if (reference.isHoistedFunction()) {
blocksWithDeclarations.add(reference.getBasicBlock());
isDeclaredInScope = true;
hoistedFn = reference;
break;
} else if (NodeUtil.isFunctionDeclaration(
reference.getNode().getParent())) {
isUnhoistedNamedFunction = true;
}
}
for (Reference reference : references) {
if (reference == hoistedFn) {
continue;
}
BasicBlock basicBlock = reference.getBasicBlock();
boolean isDeclaration = reference.isDeclaration();
boolean allowDupe =
SyntacticScopeCreator.hasDuplicateDeclarationSuppression(
reference.getNode(), v);
if (isDeclaration && !allowDupe) {
// Look through all the declarations we've found so far, and
// check if any of them are before this block.
for (BasicBlock declaredBlock : blocksWithDeclarations) {
if (declaredBlock.provablyExecutesBefore(basicBlock)) {
// TODO(johnlenz): Fix AST generating clients that so they would
// have property StaticSourceFile attached at each node. Or
// better yet, make sure the generated code never violates
// the requirement to pass aggressive var check!
String filename = NodeUtil.getSourceName(reference.getNode());
compiler.report(
JSError.make(filename,
reference.getNode(),
checkLevel,
REDECLARED_VARIABLE, v.name));
break;
}
}
}
if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) {
// Only allow an unhoisted named function to be used within the
// block it is declared.
for (BasicBlock declaredBlock : blocksWithDeclarations) {
if (!declaredBlock.provablyExecutesBefore(basicBlock)) {
String filename = NodeUtil.getSourceName(reference.getNode());
compiler.report(
JSError.make(filename,
reference.getNode(),
AMBIGUOUS_FUNCTION_DECL, v.name));
break;
}
}
}
if (!isDeclaration && !isDeclaredInScope) {
// Don't check the order of refer in externs files.
if (!reference.getNode().isFromExterns()) {
// Special case to deal with var goog = goog || {}
Node grandparent = reference
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.getGrandparent();
if (grandparent.isName()
&& grandparent.getString() == v.name) {
continue;
}
// Only generate warnings if the scopes do not match in order
// to deal with possible forward declarations and recursion
if (reference.getScope() == v.scope) {
String filename = NodeUtil.getSourceName(reference.getNode());
compiler.report(
JSError.make(filename,
reference.getNode(),
checkLevel,
UNDECLARED_REFERENCE, v.name));
}
}
}
if (isDeclaration) {
blocksWithDeclarations.add(basicBlock);
isDeclaredInScope = true;
}
}
}
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.getErrors();
}
/**
* Returns the array of warnings (never null).
*/
public JSError[] getWarnings() {
return errorManager.getWarnings();
}
@Override
public Node getRoot() {
return externAndJsRoot;
}
/**
* Creates a new id for making unique names.
*/
private int nextUniqueNameId() {
return uniqueNameId++;
}
/**
* Resets the unique name id counter
*/
@VisibleForTesting
void resetUniqueNameId() {
uniqueNameId = 0;
}
@Override
Supplier<String> getUniqueNameIdSupplier() {
final Compiler self = this;
return new Supplier<String>() {
@Override
public String get() {
return String.valueOf(self.nextUniqueNameId());
}
};
}
@Override
boolean areNodesEqualForInlining(Node n1, Node n2) {
if (options.ambiguateProperties ||
options.disambiguateProperties) {
// The type based optimizations require that type information is preserved
// during other optimizations.
return n1.isEquivalentToTyped(n2);
} else {
return n1.isEquivalentTo(n2);
}
}
//------------------------------------------------------------------------
// Inputs
//------------------------------------------------------------------------
// TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler
// interface, and which ones should always be injected.
@Override
public CompilerInput getInput(InputId id) {
return inputsById.get(id);
}
/**
* Removes an input file from AST.
* @param id The id of the input to be removed.
*/
protected void removeExternInput(InputId id) {
CompilerInput input = getInput(id);
if (input == null) {
return;
}
Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName());
inputsById.remove(id);
externs.remove(input);
Node root = input.getAstRoot(this);
if (root != null) {
root.detachFromParent();
}
}
@Override
public CompilerInput newExternInput(String name) {
SourceAst ast = new SyntheticAst(name);
if
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
/**
* Converts the parse tree for each input in a module back to js code.
*/
public String[] toSourceArray(final JSModule module) {
return runInCompilerThread(new Callable<String[]>() {
@Override
public String[] call() throws Exception {
List<CompilerInput> inputs = module.getInputs();
int numInputs = inputs.size();
if (numInputs == 0) {
return new String[0];
}
String[] sources = new String[numInputs];
CodeBuilder cb = new CodeBuilder();
for (int i = 0; i < numInputs; i++) {
Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
if (scriptNode == null) {
throw new IllegalArgumentException(
"Bad module input: " + inputs.get(i).getName());
}
cb.reset();
toSource(cb, i, scriptNode);
sources[i] = cb.toString();
}
return sources;
}
});
}
/**
* Writes out js code from a root node. If printing input delimiters, this
* method will attach a comment to the start of the text indicating which
* input the output derived from. If there were any preserve annotations
* within the root's source, they will also be printed in a block comment
* at the beginning of the output.
*/
public void toSource(final CodeBuilder cb,
final int inputSeqNum,
final Node root) {
runInCompilerThread(new Callable<Void>() {
@Override
public Void call() throws Exception {
if (options.printInputDelimiter) {
if ((cb.getLength() > 0) && !cb.endsWith("\n")) {
cb.append("\n"); // Make sure that the label starts on a new line
}
Preconditions.checkState(root.isScript());
String delimiter = options.inputDelimiter;
String inputName = root.getInputId().getIdName();
String sourceName = root.getSourceFileName();
Preconditions.checkState(sourceName != null);
Preconditions.checkState(!sourceName.isEmpty());
delimiter = delimiter
.replaceAll("%name%", Matcher.quoteReplacement(inputName))
.replaceAll("%num%", String.valueOf(inputSeqNum));
cb.append(delimiter)
.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>append("\n");
}
if (root.getJSDocInfo() != null &&
root.getJSDocInfo().getLicense() != null) {
cb.append("/*\n")
.append(root.getJSDocInfo().getLicense())
.append("*/\n");
}
// If there is a valid source map, then indicate to it that the current
// root node's mappings are offset by the given string builder buffer.
if (options.sourceMapOutputPath != null) {
sourceMap.setStartingPosition(
cb.getLineIndex(), cb.getColumnIndex());
}
// if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict'
// for the first input file
String code = toSource(root, sourceMap, inputSeqNum == 0);
if (!code.isEmpty()) {
cb.append(code);
// In order to avoid parse ambiguity when files are concatenated
// together, all files should end in a semi-colon. Do a quick
// heuristic check if there's an obvious semi-colon already there.
int length = code.length();
char lastChar = code.charAt(length - 1);
char secondLastChar = length >= 2 ?
code.charAt(length - 2) : '\0';
boolean hasSemiColon = lastChar == ';' ||
(lastChar == '\n' && secondLastChar == ';');
if (!hasSemiColon) {
cb.append(";");
}
}
return null;
}
});
}
/**
* Generates JavaScript source code for an AST, doesn't generate source
* map info.
*/
@Override
String toSource(Node n) {
initCompilerOptionsIfTesting();
return toSource(n, null, true);
}
/**
* Generates JavaScript source code for an AST.
*/
private String toSource(Node n, SourceMap sourceMap, boolean firstOutput) {
CodePrinter.Builder builder = new CodePrinter.Builder(n);
builder.setPrettyPrint(options.prettyPrint);
builder.setLineBreak(options.lineBreak);
builder.setSourceMap(sourceMap);
builder.setSourceMapDetailLevel(options.sourceMapDetailLevel);
builder.setTagAsStrict(firstOutput &&
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT);
builder.setLineLengthThreshold(options.lineLengthThreshold);
Charset charset = options.outputCharset != null ?
Charset.forName(options.outputCharset) : null;
builder.setOutputCharset(charset);
return builder.build();
}
/**
* Stores a buffer of text to which more can be appended. This is just like a
* StringBuilder except that we also track the number of lines.
*/
public static class CodeBuilder {
private final StringBuilder sb = new StringBuilder();
private int lineCount = 0;
private int colCount = 0;
/** Removes all text, but leaves the line count unchanged. */
void reset() {
sb.setLength(0);
}
/** Appends the given string to the text buffer. */
CodeBuilder append(String str) {
sb.append(str);
// Adjust the line and column information for the new text.
int index = -1;
int lastIndex = index;
while ((index = str.indexOf('\n', index + 1)) >= 0) {
++lineCount;
lastIndex = index;
}
if (lastIndex == -1) {
// No new lines, append the new characters added.
colCount += str.length();
} else {
colCount = str.length() - (lastIndex + 1);
}
return this;
}
/** Returns all text in the text buffer. */
@Override
public String toString() {
return sb.toString();
}
/** Returns the length of the text buffer. */
public int getLength() {
return sb.length();
}
/** Returns the (zero-based) index of the last line in the text buffer. */
int getLineIndex() {
return lineCount;
}
/** Returns the (zero-based) index of the last column in the text buffer. */
int getColumnIndex() {
return colCount;
}
/** Determines whether the text ends with the given suffix. */
boolean endsWith(String suffix) {
return (sb.length() > suffix.length())
&& suffix.equals(sb.substring(sb.length() - suffix.length()));
}
}
//------------------------------------------------------------------------
// Optimizations
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> //------------------------------------------------------------------------
public void optimize() {
// Ideally, this pass should be the first pass run, however:
// 1) VariableReferenceCheck reports unexpected warnings if Normalize
// is done first.
// 2) ReplaceMessages, stripCode, and potentially custom passes rely on
// unmodified local names.
normalize();
PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker);
if (options.devMode == DevMode.EVERY_PASS) {
phaseOptimizer.setSanityCheck(sanityCheck);
}
phaseOptimizer.consume(getPassConfig().getOptimizations());
phaseOptimizer.process(externsRoot, jsRoot);
if (hasErrors()) {
return;
}
}
@Override
void setCssRenamingMap(CssRenamingMap map) {
options.cssRenamingMap = map;
}
@Override
CssRenamingMap getCssRenamingMap() {
return options.cssRenamingMap;
}
/**
* Reprocesses the current defines over the AST. This is used by GwtCompiler
* to generate N outputs for different targets from the same (checked) AST.
* For each target, we apply the target-specific defines by calling
* {@code processDefines} and then {@code optimize} to optimize the AST
* specifically for that target.
*/
public void processDefines() {
(new DefaultPassConfig(options)).processDefines.create(this)
.process(externsRoot, jsRoot);
}
boolean isInliningForbidden() {
return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC ||
options.propertyRenaming ==
PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC;
}
/** Control Flow Analysis. */
ControlFlowGraph<Node> computeCFG() {
logger.info("Computing Control Flow Graph");
Tracer tracer = newTracer("computeCFG");
ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false);
process(cfa);
stopTracer(tracer, "computeCFG");
return cfa.getCfg();
}
public void normalize() {
logger.info("Normalizing");
startPass("normalize");
process(new Normalize(this, false));
endPass();
}
@Override
void prepareAst(Node root) {
Compiler
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Pass pass = new PrepareAst(this);
pass.process(null, root);
}
void recordFunctionInformation() {
logger.info("Recording function information");
startPass("recordFunctionInformation");
RecordFunctionInformation recordFunctionInfoPass =
new RecordFunctionInformation(
this, getPassConfig().getIntermediateState().functionNames);
process(recordFunctionInfoPass);
functionInformationMap = recordFunctionInfoPass.getMap();
endPass();
}
protected final CodeChangeHandler.RecentChange recentChange =
new CodeChangeHandler.RecentChange();
private final List<CodeChangeHandler> codeChangeHandlers =
Lists.<CodeChangeHandler>newArrayList();
@Override
void addChangeHandler(CodeChangeHandler handler) {
codeChangeHandlers.add(handler);
}
@Override
void removeChangeHandler(CodeChangeHandler handler) {
codeChangeHandlers.remove(handler);
}
/**
* All passes should call reportCodeChange() when they alter
* the JS tree structure. This is verified by CompilerTestCase.
* This allows us to optimize to a fixed point.
*/
@Override
public void reportCodeChange() {
for (CodeChangeHandler handler : codeChangeHandlers) {
handler.reportChange();
}
}
@Override
public CodingConvention getCodingConvention() {
CodingConvention convention = options.getCodingConvention();
convention = convention != null ? convention : defaultCodingConvention;
return convention;
}
@Override
public boolean isIdeMode() {
return options.ideMode;
}
@Override
public boolean acceptEcmaScript5() {
switch (options.getLanguageIn()) {
case ECMASCRIPT5:
case ECMASCRIPT5_STRICT:
return true;
}
return false;
}
public LanguageMode languageMode() {
return options.getLanguageIn();
}
@Override
public boolean acceptConstKeyword() {
return options.acceptConstKeyword;
}
@Override
Config getParserConfig() {
if (parserConfig == null) {
Config.LanguageMode mode;
switch (options.getLanguageIn()) {
case ECMASCRIPT3:
mode = Config.LanguageMode.ECMASCRIPT3;
break;
case ECMASCRIPT5:
mode = Config.LanguageMode.ECMASCRIPT5;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.parsing.ParserRunner;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import java.io.IOException;
import java.util.logging.Logger;
/**
* Generates an AST for a JavaScript source file.
*
*/
public class JsAst implements SourceAst {
private static final Logger logger_ = Logger.getLogger(JsAst.class.getName());
private static final long serialVersionUID = 1L;
private transient InputId inputId;
private transient SourceFile sourceFile;
private String fileName;
private Node root;
public JsAst(SourceFile sourceFile) {
this.inputId = new InputId(sourceFile.getName());
this.sourceFile = sourceFile;
this.fileName = sourceFile.getName();
}
@Override
public Node getAstRoot(AbstractCompiler compiler) {
if (root == null) {
parse(compiler);
root.setInputId(inputId);
}
return root;
}
@Override
public void clearAst() {
root = null;
// While we're at it, clear out any saved text in the source file on
// the assumption that if we're dumping the parse tree, then we probably
// assume regenerating everything else is a smart idea also.
sourceFile.clearCachedSource();
}
@Override
public InputId getInputId() {
return inputId;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> }
@Override
public SourceFile getSourceFile() {
return sourceFile;
}
@Override
public void setSourceFile(SourceFile file) {
Preconditions.checkState(fileName.equals(file.getName()));
sourceFile = file;
}
private void parse(AbstractCompiler compiler) {
try {
logger_.fine("Parsing: " + sourceFile.getName());
root = ParserRunner.parse(sourceFile, sourceFile.getCode(),
compiler.getParserConfig(),
compiler.getDefaultErrorReporter(),
logger_);
} catch (IOException e) {
compiler.report(
JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName()));
}
if (root == null || compiler.hasHaltingErrors()) {
// There was a parse error or IOException, so use a dummy block.
root = IR.script();
} else {
compiler.prepareAst(root);
}
// Set the source name so that the compiler passes can track
// the source file and module.
root.setStaticSourceFile(sourceFile);
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>rhino.Node;
import com.google.javascript.rhino.jstype.StaticReference;
import com.google.javascript.rhino.jstype.StaticSlot;
import java.io.Serializable;
import java.util.Set;
/**
* Object type.
*
* In JavaScript, all object types have properties, and each of those
* properties has a type. Property types may be DECLARED, INFERRED, or
* UNKNOWN.
*
* DECLARED properties have an explicit type annotation, as in:
* <code>
* /xx @type {number} x/
* Foo.prototype.bar = 1;
* </code>
* This property may only hold number values, and an assignment to any
* other type of value is an error.
*
* INFERRED properties do not have an explicit type annotation. Rather,
* we try to find all the possible types that this property can hold.
* <code>
* Foo.prototype.bar = 1;
* </code>
* If the programmer assigns other types of values to this property,
* the property will take on the union of all these types.
*
* UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN
* type has all properties, but we do not know whether they are
* declared or inferred.
*
*/
public abstract class ObjectType extends JSType implements StaticScope<JSType> {
private boolean visited;
private JSDocInfo docInfo = null;
private boolean unknown = true;
ObjectType(JSTypeRegistry registry) {
super(registry);
}
@Override
public Node getRootNode() { return null; }
@Override
public ObjectType getParentScope() {
return getImplicitPrototype();
}
@Override
public abstract Property getSlot(String name);
@Override
public Property getOwnSlot(String name) {
if (hasOwnProperty(name)) {
return getSlot(name);
}
return null;
}
@Override
public ObjectType getTypeOfThis() {
return null;
}
/**
* Gets the declared default element type.
* @see ParameterizedType
*/
public JSType getParameterType() {
return null;
}
/**
* Gets the declared default index type.
* @see IndexedType
*/
public
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> name does not have these suffixes, and as such,
* recollapses these implicit types back to their real type.
*/
public String getNormalizedReferenceName() {
String name = getReferenceName();
if (name != null) {
int pos = name.indexOf("(");
if (pos != -1) {
return name.substring(0, pos);
}
}
return name;
}
@Override
public String getDisplayName() {
return getNormalizedReferenceName();
}
/**
* Creates a suffix for a proxy delegate.
* @see #getNormalizedReferenceName
*/
public static String createDelegateSuffix(String suffix) {
return "(" + suffix + ")";
}
/**
* Returns true if the object is named.
* @return true if the object is named, false if it is anonymous
*/
public boolean hasReferenceName() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
// super
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
// objects are comparable to everything but null/undefined
if (that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
} else {
return FALSE;
}
}
/**
* Gets this object's constructor.
* @return this object's constructor or {@code null} if it is a native
* object (constructed natively v.s. by instantiation of a function)
*/
public abstract FunctionType getConstructor();
/**
* Gets the implicit prototype (a.k.a. the {@code [[Prototype]]} property).
*/
public abstract ObjectType getImplicitPrototype();
/**
* Defines a property whose type is synthesized (i.e. not inferred).
* @param propertyName the property's name
* @param type the type
* @param propertyNode the node corresponding to the declaration of property
* which might later be accessed using {@code getPropertyNode}.
*/
public final boolean defineDeclaredProperty(String propertyName,
JSType type, Node propertyNode) {
boolean result = defineProperty(propertyName, type, false,
propertyNode);
// All property definitions go through this method
// or define
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2011 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* This class walks the AST and validates that the structure is correct.
*
* @author johnlenz@google.com (John Lenz)
*/
public class AstValidator implements CompilerPass {
// Possible enhancements:
// * verify NAME, LABEL_NAME, GETPROP property name and unquoted
// object-literal keys are valid javascript identifiers.
// * optionally verify every node has source location information.
// * optionally verify every node has an assigned JSType
//
public interface ViolationHandler {
void handleViolation(String message, Node n);
}
private final ViolationHandler violationHandler;
public AstValidator(ViolationHandler handler) {
this.violationHandler = handler;
}
public AstValidator() {
this.violationHandler = new ViolationHandler() {
@Override
public void handleViolation(String message, Node n) {
throw new IllegalStateException(
message + " Reference node " + n.toString());
}
};
}
@Override
public void process(Node externs, Node root) {
if (externs != null) {
validateCodeRoot(externs);
}
if (root != null) {
validateCodeRoot(root);
}
}
public void validateRoot(Node n) {
validateNodeType(Token.BLOCK, n);
validateIsSynthetic(n);
validateChildCount(n, 2);
validateCodeRoot
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> return;
case Token.FUNCTION:
if (NodeUtil.isFunctionExpression(n)) {
return;
}
String fnName = n.getFirstChild().getString();
if (fnName.isEmpty()) {
// This is invalid, but allow it so the checks can catch it.
return;
}
declareVar(n.getFirstChild());
return; // should not examine function's children
case Token.CATCH:
Preconditions.checkState(n.getChildCount() == 2);
Preconditions.checkState(n.getFirstChild().isName());
// the first child is the catch var and the third child
// is the code block
final Node var = n.getFirstChild();
final Node block = var.getNext();
declareVar(var);
scanVars(block, n);
return; // only one child to scan
case Token.SCRIPT:
inputId = n.getInputId();
Preconditions.checkNotNull(inputId);
break;
}
// Variables can only occur in statement-level nodes, so
// we only need to traverse children in a couple special cases.
if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) {
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
scanVars(child, n);
child = next;
}
}
}
/**
* Interface for injectable duplicate handling.
*/
interface RedeclarationHandler {
void onRedeclaration(
Scope s, String name, Node n, CompilerInput input);
}
/**
* The default handler for duplicate declarations.
*/
private class DefaultRedeclarationHandler implements RedeclarationHandler {
@Override
public void onRedeclaration(
Scope s, String name, Node n, CompilerInput input) {
Node parent = n.getParent();
// Don't allow multiple variables to be declared at the top level scope
if (scope.isGlobal()) {
Scope.Var origVar = scope.getVar(name);
Node origParent = origVar.getParentNode();
if (origParent.isCatch() &&
parent.isCatch()) {
// Okay, both are 'catch(x)' variables.
return;
}
boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar);
if (!allow
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2011 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.DiagnosticType;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
/**
* {@link CheckDebuggerStatement} checks for the presence of the "debugger"
* statement in JavaScript code. It is appropriate to use this statement while
* developing JavaScript; however, it is generally undesirable to include it in
* production code.
*
* @author bolinfest@google.com (Michael Bolin)
*/
class CheckDebuggerStatement extends AbstractPostOrderCallback
implements CompilerPass {
static final DiagnosticType DEBUGGER_STATEMENT_PRESENT =
DiagnosticType.disabled("JSC_DEBUGGER_STATEMENT_PRESENT",
"Using the debugger statement can halt your application if the user " +
"has a JavaScript debugger running.");
private final AbstractCompiler compiler;
public CheckDebuggerStatement(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isDebugger()) {
t.report(n, DEBUGGER_STATEMENT_PRESENT);
}
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> final DiagnosticType ARGUMENTS_DECLARATION = DiagnosticType.warning(
"JSC_ARGUMENTS_DECLARATION",
"\"arguments\" cannot be redeclared in ES5 strict mode");
static final DiagnosticType ARGUMENTS_ASSIGNMENT = DiagnosticType.warning(
"JSC_ARGUMENTS_ASSIGNMENT",
"the \"arguments\" object cannot be reassigned in ES5 strict mode");
static final DiagnosticType DELETE_VARIABLE = DiagnosticType.warning(
"JSC_DELETE_VARIABLE",
"variables, functions, and arguments cannot be deleted in "
+ "ES5 strict mode");
static final DiagnosticType ILLEGAL_NAME = DiagnosticType.error(
"JSC_ILLEGAL_NAME",
"identifiers ending in '__' cannot be used in Caja");
static final DiagnosticType DUPLICATE_OBJECT_KEY = DiagnosticType.warning(
"JSC_DUPLICATE_OBJECT_KEY",
"object literals cannot contain duplicate keys in ES5 strict mode");
private final AbstractCompiler compiler;
private final boolean noVarCheck;
private final boolean noCajaChecks;
StrictModeCheck(AbstractCompiler compiler) {
this(compiler, false, false);
}
StrictModeCheck(
AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) {
this.compiler = compiler;
this.noVarCheck = noVarCheck;
this.noCajaChecks = noCajaChecks;
}
@Override public void process(Node externs, Node root) {
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
NodeTraversal.traverse(compiler, root, new NonExternChecks());
}
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
if (!isDeclaration(n)) {
checkNameUse(t, n);
}
} else if (n.isAssign()) {
checkAssignment(t, n);
} else if (n.isDelProp()) {
checkDelete(t, n);
} else if (n.isObjectLit()) {
checkObjectLiteral(t, n);
} else if (n.isLabel()) {
checkLabel(t, n);
}
}
/**
* Determines if the given name
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> is a declaration, which can be a declaration
* of a variable, function, or argument.
*/
private static boolean isDeclaration(Node n) {
switch (n.getParent().getType()) {
case Token.VAR:
case Token.FUNCTION:
case Token.CATCH:
return true;
case Token.PARAM_LIST:
return n.getParent().getParent().isFunction();
default:
return false;
}
}
/** Checks that the given name is used legally. */
private void checkNameUse(NodeTraversal t, Node n) {
Var v = t.getScope().getVar(n.getString());
if (v == null) {
// In particular, this prevents creating a global variable by assigning
// to it without a declaration.
if (!noVarCheck) {
t.report(n, UNKNOWN_VARIABLE, n.getString());
}
}
if (!noCajaChecks) {
if ("eval".equals(n.getString())) {
t.report(n, EVAL_USE);
} else if (n.getString().endsWith("__")) {
t.report(n, ILLEGAL_NAME);
}
}
}
/** Checks that an assignment is not to the "arguments" object. */
private void checkAssignment(NodeTraversal t, Node n) {
if (n.getFirstChild().isName()) {
if ("arguments".equals(n.getFirstChild().getString())) {
t.report(n, ARGUMENTS_ASSIGNMENT);
} else if ("eval".equals(n.getFirstChild().getString())) {
// Note that assignment to eval is already illegal because any use of
// that name is illegal.
if (noCajaChecks) {
t.report(n, EVAL_ASSIGNMENT);
}
}
}
}
/** Checks that variables, functions, and arguments are not deleted. */
private void checkDelete(NodeTraversal t, Node n) {
if (n.getFirstChild().isName()) {
Var v = t.getScope().getVar(n.getFirstChild().getString());
if (v != null) {
t.report(n, DELETE_VARIABLE);
}
}
}
/** Checks that object literal keys are valid. */
private void checkObjectLiteral(NodeTraversal t, Node n) {
Set
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS><String> getters = Sets.newHashSet();
Set<String> setters = Sets.newHashSet();
for (Node key = n.getFirstChild();
key != null;
key = key.getNext()) {
if (!noCajaChecks && key.getString().endsWith("__")) {
t.report(key, ILLEGAL_NAME);
}
if (!key.isSetterDef()) {
// normal property and getter cases
if (getters.contains(key.getString())) {
t.report(key, DUPLICATE_OBJECT_KEY);
} else {
getters.add(key.getString());
}
}
if (!key.isGetterDef()) {
// normal property and setter cases
if (setters.contains(key.getString())) {
t.report(key, DUPLICATE_OBJECT_KEY);
} else {
setters.add(key.getString());
}
}
}
}
/** Checks that label names are valid. */
private void checkLabel(NodeTraversal t, Node n) {
if (n.getFirstChild().getString().endsWith("__")) {
if (!noCajaChecks) {
t.report(n.getFirstChild(), ILLEGAL_NAME);
}
}
}
/** Checks that are performed on non-extern code only. */
private class NonExternChecks extends AbstractPostOrderCallback {
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if ((n.isName()) && isDeclaration(n)) {
checkDeclaration(t, n);
} else if (n.isGetProp()) {
checkProperty(t, n);
}
}
/** Checks for illegal declarations. */
private void checkDeclaration(NodeTraversal t, Node n) {
if ("eval".equals(n.getString())) {
t.report(n, EVAL_DECLARATION);
} else if ("arguments".equals(n.getString())) {
t.report(n, ARGUMENTS_DECLARATION);
} else if (n.getString().endsWith("__")) {
if (!noCajaChecks) {
t.report(n, ILLEGAL_NAME);
}
}
}
/** Checks for illegal property accesses. */
private void checkProperty(NodeTraversal t, Node n
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>) {
if (n.getLastChild().getString().endsWith("__")) {
if (!noCajaChecks) {
t.report(n.getLastChild(), ILLEGAL_NAME);
}
}
}
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> private final JsMessage.IdGenerator idGenerator;
final AbstractCompiler compiler;
/**
* The names encountered associated with their defining node and source. We
* use it for tracking duplicated message ids in the source code.
*/
private final Map<String, MessageLocation> messageNames =
Maps.newHashMap();
/**
* List of found goog.getMsg call nodes.
*
* When we visit goog.getMsg() node we add a pair node:sourcename and later
* when we visit its parent we remove this pair. All nodes that are left at
* the end of traversing are orphaned nodes. It means have no corresponding
* var or property node.
*/
private final Map<Node, String> googMsgNodes = Maps.newHashMap();
private final CheckLevel checkLevel;
/**
* Creates js message visitor.
*
* @param compiler the compiler instance
* @param needToCheckDuplications whether to check duplicated messages in
* traversed
* @param style style that should be used during parsing
* @param idGenerator generator that used for creating unique ID for the
* message
*/
JsMessageVisitor(AbstractCompiler compiler,
boolean needToCheckDuplications,
JsMessage.Style style, JsMessage.IdGenerator idGenerator) {
this.compiler = compiler;
this.needToCheckDuplications = needToCheckDuplications;
this.style = style;
this.idGenerator = idGenerator;
checkLevel = (style == JsMessage.Style.CLOSURE)
? CheckLevel.ERROR : CheckLevel.WARNING;
// TODO(anatol): add flag that decides whether to process UNNAMED messages.
// Some projects would not want such functionality (unnamed) as they dont
// use SOY templates.
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
for (Map.Entry<Node, String> msgNode : googMsgNodes.entrySet()) {
compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(),
checkLevel, MESSAGE_NODE_IS_ORPHANED));
}
}
@Override
public void visit(NodeTraversal traversal, Node node, Node parent) {
String messageKey;
boolean isVar;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Node} does not
* correspond to a valid js message node
*/
private void extractMessageFromProperty(
Builder builder, Node getPropNode, Node assignNode)
throws MalformedException {
Node callNode = getPropNode.getNext();
maybeInitMetaDataFromJsDoc(builder, assignNode);
extractFromCallNode(builder, callNode);
}
/**
* Initializes the meta data in a JsMessage by examining the nodes just before
* and after a message VAR node.
*
* @param builder the message builder whose meta data will be initialized
* @param varNode the message VAR node
* @param parentOfVarNode {@code varNode}'s parent node
*/
private void maybeInitMetaDataFromJsDocOrHelpVar(
Builder builder, Node varNode, @Nullable Node parentOfVarNode)
throws MalformedException {
// First check description in @desc
if (maybeInitMetaDataFromJsDoc(builder, varNode)) {
return;
}
// Check the preceding node for meta data
if ((parentOfVarNode != null) &&
maybeInitMetaDataFromHelpVar(builder,
parentOfVarNode.getChildBefore(varNode))) {
return;
}
// Check the subsequent node for meta data
maybeInitMetaDataFromHelpVar(builder, varNode.getNext());
}
/**
* Initializes the meta data in a JsMessage by examining a node just before or
* after a message VAR node.
*
* @param builder the message builder whose meta data will be initialized
* @param sibling a node adjacent to the message VAR node
* @return true iff message has corresponding description variable
*/
private boolean maybeInitMetaDataFromHelpVar(Builder builder,
@Nullable Node sibling) throws MalformedException {
if ((sibling != null) && (sibling.isVar())) {
Node nameNode = sibling.getFirstChild();
String name = nameNode.getString();
if (name.equals(builder.getKey() + DESC_SUFFIX)) {
Node valueNode = nameNode.getFirstChild();
String desc = extractStringFromStringExprNode(valueNode);
if (desc.startsWith(HIDDEN_DESC_PREFIX)) {
builder.setDesc(desc.substring(HIDDEN_DESC_PREFIX.length()).trim());
builder.setIsHidden(true);
} else {
builder
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> * an unregistered placeholder
*/
private void parseMessageTextNode(Builder builder, Node node)
throws MalformedException {
String value = extractStringFromStringExprNode(node);
while(true) {
int phBegin = value.indexOf(PH_JS_PREFIX);
if (phBegin < 0) {
// Just a string literal
builder.appendStringPart(value);
return;
} else {
if (phBegin > 0) {
// A string literal followed by a placeholder
builder.appendStringPart(value.substring(0, phBegin));
}
// A placeholder. Find where it ends
int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin);
if (phEnd < 0) {
throw new MalformedException(
"Placeholder incorrectly formatted in: " + builder.getKey(),
node);
}
String phName = value.substring(phBegin + PH_JS_PREFIX.length(),
phEnd);
builder.appendPlaceholderReference(phName);
int nextPos = phEnd + PH_JS_SUFFIX.length();
if (nextPos < value.length()) {
// Iterate on the rest of the message value
value = value.substring(nextPos);
} else {
// The message is parsed
return;
}
}
}
}
/**
* Processes found js message. Several examples of "standard" processing
* routines are:
* <ol>
* <li>extract all js messages
* <li>replace js messages with localized versions for some specific language
* <li>check that messages have correct syntax and present in localization
* bundle
* </ol>
*
* @param message the found message
* @param definition the definition of the object and usually contains all
* additional message information like message node/parent's node
*/
abstract void processJsMessage(JsMessage message,
JsMessageDefinition definition);
/**
* Returns whether the given js identifier is a valid js message name.
*/
boolean isMessageName(String identifier, boolean isNewStyleMessage) {
return identifier.startsWith(MSG_PREFIX) &&
(style == JsMessage.Style.CLOSURE || isNewStyleMessage ||
!identifier.endsWith(DESC_SUFFIX));
}
/**
* Returns whether the
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> given message name is in the unnamed namespace.
*/
private static boolean isUnnamedMessageName(String identifier) {
return MSG_UNNAMED_PATTERN.matcher(identifier).matches();
}
/**
* Returns whether a string is nonempty, begins with a lowercase letter, and
* contains only digits and underscores after the first underscore.
*/
static boolean isLowerCamelCaseWithNumericSuffixes(String input) {
return CAMELCASE_PATTERN.matcher(input).matches();
}
/**
* Returns human-readable name of the given node's type.
*/
private static String getReadableTokenName(Node node) {
return Token.name(node.getType());
}
/**
* Converts the given string from upper-underscore case to lower-camel case,
* preserving numeric suffixes. For example: "NAME" -> "name" "A4_LETTER" ->
* "a4Letter" "START_SPAN_1_23" -> "startSpan_1_23".
*/
static String toLowerCamelCaseWithNumericSuffixes(String input) {
// Determine where the numeric suffixes begin
int suffixStart = input.length();
while (suffixStart > 0) {
char ch = '\0';
int numberStart = suffixStart;
while (numberStart > 0) {
ch = input.charAt(numberStart - 1);
if (Character.isDigit(ch)) {
numberStart--;
} else {
break;
}
}
if ((numberStart > 0) && (numberStart < suffixStart) && (ch == '_')) {
suffixStart = numberStart - 1;
} else {
break;
}
}
if (suffixStart == input.length()) {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input);
} else {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,
input.substring(0, suffixStart)) +
input.substring(suffixStart);
}
}
static class MalformedException extends Exception {
private static final long serialVersionUID = 1L;
private final Node node;
MalformedException(String message, Node node) {
super(message);
this.node =
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> LinkedHashMultimap.create();
} else {
this.invalidationMap = null;
}
}
@Override
public void process(Node externs, Node root) {
for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) {
addInvalidatingType(mis.typeA);
addInvalidatingType(mis.typeB);
recordInvalidationError(mis.typeA, mis.src);
recordInvalidationError(mis.typeB, mis.src);
}
StaticScope<T> scope = typeSystem.getRootScope();
NodeTraversal.traverse(compiler, externs, new FindExternProperties());
NodeTraversal.traverse(compiler, root, new FindRenameableProperties());
renameProperties();
}
private void recordInvalidationError(JSType t, JSError error) {
if (!t.isObject()) {
return;
}
if (t.isUnionType()) {
for (JSType alt : t.toMaybeUnionType().getAlternates()) {
recordInvalidationError(alt, error);
}
return;
}
if (invalidationMap != null) {
invalidationMap.put(t, error);
}
}
/**
* Invalidates the given type, so that no properties on it will be renamed.
*/
private void addInvalidatingType(JSType type) {
type = type.restrictByNotNullOrUndefined();
if (type.isUnionType()) {
for (JSType alt : type.toMaybeUnionType().getAlternates()) {
addInvalidatingType(alt);
}
} else if (type.isEnumElementType()) {
addInvalidatingType(type.toMaybeEnumElementType().getPrimitiveType());
} else {
typeSystem.addInvalidatingType(type);
ObjectType objType = ObjectType.cast(type);
if (objType != null && objType.getImplicitPrototype() != null) {
typeSystem.addInvalidatingType(objType.getImplicitPrototype());
}
}
}
/** Returns the property for the given name, creating it if necessary. */
protected Property getProperty(String name) {
if (!properties.containsKey(name)) {
properties.put(name, new Property(name));
}
return properties.get(name);
}
/** Public for testing
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Check for usage of 'with'.
*
*/
class ControlStructureCheck implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
static final DiagnosticType USE_OF_WITH = DiagnosticType.warning(
"JSC_USE_OF_WITH",
"The use of the 'with' structure should be avoided.");
ControlStructureCheck(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
check(root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
check(scriptRoot);
}
/**
* Reports errors for any invalid use of control structures.
*
* @param node Current node to check.
*/
private void check(Node node) {
switch (node.getType()) {
case Token.WITH:
JSDocInfo info = node.getJSDocInfo();
boolean allowWith =
info != null && info.getSuppressions().contains("with");
if (!allowWith) {
report(node, USE_OF_WITH);
}
break;
}
for (Node bChild = node.getFirstChild(); bChild != null;) {
Node next = bChild.getNext();
check(bChild);
bChild = next;
}
}
private void report(Node n, Diagnostic
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> {0} assigned a value more than once");
static final DiagnosticType CONST_PROPERTY_DELETED =
DiagnosticType.warning(
"JSC_CONSTANT_PROPERTY_DELETED",
"constant property {0} cannot be deleted");
private final AbstractCompiler compiler;
private final TypeValidator validator;
// State about the current traversal.
private int deprecatedDepth = 0;
private int methodDepth = 0;
private JSType currentClass = null;
private final Multimap<String, String> initializedConstantProperties;
CheckAccessControls(AbstractCompiler compiler) {
this.compiler = compiler;
this.validator = compiler.getTypeValidator();
this.initializedConstantProperties = HashMultimap.create();
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
NodeTraversal.traverse(compiler, scriptRoot, this);
}
@Override
public void enterScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
Node parent = n.getParent();
if (isDeprecatedFunction(n, parent)) {
deprecatedDepth++;
}
if (methodDepth == 0) {
currentClass = getClassOfMethod(n, parent);
}
methodDepth++;
}
}
@Override
public void exitScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
Node parent = n.getParent();
if (isDeprecatedFunction(n, parent)) {
deprecatedDepth--;
}
methodDepth--;
if (methodDepth == 0) {
currentClass = null;
}
}
}
/**
* Gets the type of the class that "owns" a method, or null if
* we know that its un-owned.
*/
private JSType getClassOfMethod(Node n, Node parent) {
if (parent.isAssign()) {
Node lValue = parent.getFirstChild();
if (NodeUtil.isGet(lValue)) {
// We have an assignment of the form "a.b = ...".
JSType lValueType = lValue.getJSType();
if (lValueType
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> != null && lValueType.isNominalConstructor()) {
// If a.b is a constructor, then everything in this function
// belongs to the "a.b" type.
return (lValueType.toMaybeFunctionType()).getInstanceType();
} else {
// If a.b is not a constructor, then treat this as a method
// of whatever type is on "a".
return normalizeClassType(lValue.getFirstChild().getJSType());
}
} else {
// We have an assignment of the form "a = ...", so pull the
// type off the "a".
return normalizeClassType(lValue.getJSType());
}
} else if (NodeUtil.isFunctionDeclaration(n) ||
parent.isName()) {
return normalizeClassType(n.getJSType());
}
return null;
}
/**
* Normalize the type of a constructor, its instance, and its prototype
* all down to the same type (the instance type).
*/
private JSType normalizeClassType(JSType type) {
if (type == null || type.isUnknownType()) {
return type;
} else if (type.isNominalConstructor()) {
return (type.toMaybeFunctionType()).getInstanceType();
} else if (type.isFunctionPrototypeType()) {
FunctionType owner = ((ObjectType) type).getOwnerFunction();
if (owner.isConstructor()) {
return owner.getInstanceType();
}
}
return type;
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
checkNameDeprecation(t, n, parent);
checkNameVisibility(t, n, parent);
break;
case Token.GETPROP:
checkPropertyDeprecation(t, n, parent);
checkPropertyVisibility(t, n, parent);
checkConstantProperty(t, n);
break;
case Token.NEW:
checkConstructorDeprecation(t, n, parent);
break;
}
}
/**
* Checks the given NEW node to ensure that access restrictions are obeyed.
*/
private void checkConstructorDeprecation(NodeTraversal t, Node n,
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.idGeneratorMap;
}
GlobalNamespace getGlobalNamespace() {
return namespaceForChecks;
}
PreprocessorSymbolTable getPreprocessorSymbolTable() {
return preprocessorSymbolTable;
}
void maybeInitializePreprocessorSymbolTable(AbstractCompiler compiler) {
if (options.ideMode) {
Node root = compiler.getRoot();
if (preprocessorSymbolTable == null ||
preprocessorSymbolTable.getRootNode() != root) {
preprocessorSymbolTable = new PreprocessorSymbolTable(root);
}
}
}
@Override
protected List<PassFactory> getChecks() {
List<PassFactory> checks = Lists.newArrayList();
if (options.closurePass) {
checks.add(closureGoogScopeAliases);
}
if (options.nameAnonymousFunctionsOnly) {
if (options.anonymousFunctionNaming ==
AnonymousFunctionNamingPolicy.MAPPED) {
checks.add(nameMappedAnonymousFunctions);
} else if (options.anonymousFunctionNaming ==
AnonymousFunctionNamingPolicy.UNMAPPED) {
checks.add(nameUnmappedAnonymousFunctions);
}
return checks;
}
if (options.checkSuspiciousCode ||
options.enables(DiagnosticGroups.GLOBAL_THIS) ||
options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) {
checks.add(suspiciousCode);
}
if (options.checkControlStructures
|| options.enables(DiagnosticGroups.ES5_STRICT)) {
checks.add(checkControlStructures);
}
if (options.checkRequires.isOn()) {
checks.add(checkRequires);
}
if (options.checkProvides.isOn()) {
checks.add(checkProvides);
}
// The following passes are more like "preprocessor" passes.
// It's important that they run before most checking passes.
// Perhaps this method should be renamed?
if (options.generateExports) {
checks.add(generateExports);
}
if (options.exportTestFunctions) {
checks.add(exportTestFunctions);
}
if (options.closurePass) {
checks.add(closurePrimitives.makeOneTimePass());
}
if (options.closurePass && options.checkMissingGetCssNameLevel.isOn()) {
checks.add(closureCheckGet
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> return new CheckRequiresForConstructors(compiler, options.checkRequires);
}
};
/** Makes sure @constructor is paired with goog.provides(). */
final HotSwapPassFactory checkProvides =
new HotSwapPassFactory("checkProvides", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new CheckProvides(compiler, options.checkProvides);
}
};
private static final DiagnosticType GENERATE_EXPORTS_ERROR =
DiagnosticType.error(
"JSC_GENERATE_EXPORTS_ERROR",
"Exports can only be generated if export symbol/property " +
"functions are set.");
/** Generates exports for @export annotations. */
final PassFactory generateExports =
new PassFactory("generateExports", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
CodingConvention convention = compiler.getCodingConvention();
if (convention.getExportSymbolFunction() != null &&
convention.getExportPropertyFunction() != null) {
return new GenerateExports(compiler,
convention.getExportSymbolFunction(),
convention.getExportPropertyFunction());
} else {
return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR);
}
}
};
/** Generates exports for functions associated with JSUnit. */
final PassFactory exportTestFunctions =
new PassFactory("exportTestFunctions", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
CodingConvention convention = compiler.getCodingConvention();
if (convention.getExportSymbolFunction() != null) {
return new ExportTestFunctions(compiler,
convention.getExportSymbolFunction(),
convention.getExportPropertyFunction());
} else {
return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR);
}
}
};
/** Raw exports processing pass. */
final PassFactory gatherRawExports =
new PassFactory("gatherRawExports", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
final GatherRawExports pass = new GatherRawExports(
compiler);
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
pass.process(externs, root);
if (exportedNames == null) {
exportedNames = Sets.newHashSet();
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
exportedNames.addAll(pass.getExportedVariableNames());
}
};
}
};
/** Closure pre-processing pass. */
@SuppressWarnings("deprecation")
final HotSwapPassFactory closurePrimitives =
new HotSwapPassFactory("processProvidesAndRequires", false) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
maybeInitializePreprocessorSymbolTable(compiler);
final ProcessClosurePrimitives pass = new ProcessClosurePrimitives(
compiler,
preprocessorSymbolTable,
options.brokenClosureRequiresLevel,
options.rewriteNewDateGoogNow);
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
pass.process(externs, root);
exportedNames = pass.getExportedVariableNames();
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
pass.hotSwapScript(scriptRoot, originalRoot);
}
};
}
};
/**
* The default i18n pass.
* A lot of the options are not configurable, because ReplaceMessages
* has a lot of legacy logic.
*/
final PassFactory replaceMessages =
new PassFactory("replaceMessages", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new ReplaceMessages(compiler,
options.messageBundle,
/* warn about message dupes */
true,
/* allow messages with goog.getMsg */
JsMessage.Style.getFromParams(true, false),
/* if we can't find a translation, don't worry about it. */
false);
}
};
/** Applies aliases and inlines goog.scope. */
final HotSwapPassFactory closureGoogScopeAliases =
new HotSwapPassFactory("processGoogScopeAliases", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
maybeInitializePreprocessorSymbolTable(compiler);
return new ScopedAliases(
compiler,
preprocessorSymbolTable,
options.getAliasTransformationHandler());
}
};
/** Checks that CSS class names are wrapped in goog.getCssName */
final PassFactory closureCheckGetCssName =
new PassFactory("checkMissingGetCssName", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
String
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Factory("latePeepholeOptimizations", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
final boolean late = true;
return new PeepholeOptimizationsPass(compiler,
new StatementFusion(),
new PeepholeRemoveDeadCode(),
new PeepholeSubstituteAlternateSyntax(late),
new PeepholeReplaceKnownMethods(),
new PeepholeFoldConstants(late),
new ReorderConstantExpression());
}
};
/** Checks that all variables are defined. */
final HotSwapPassFactory checkVars =
new HotSwapPassFactory("checkVars", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new VarCheck(compiler);
}
};
/** Checks for RegExp references. */
final PassFactory checkRegExp =
new PassFactory("checkRegExp", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
final CheckRegExp pass = new CheckRegExp(compiler);
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
pass.process(externs, root);
compiler.setHasRegExpGlobalReferences(
pass.isGlobalRegExpPropertiesUsed());
}
};
}
};
/** Checks that references to variables look reasonable. */
final HotSwapPassFactory checkVariableReferences =
new HotSwapPassFactory("checkVariableReferences", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new VariableReferenceCheck(
compiler, options.aggressiveVarCheck);
}
};
/** Pre-process goog.testing.ObjectPropertyString. */
final PassFactory objectPropertyStringPreprocess =
new PassFactory("ObjectPropertyStringPreprocess", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new ObjectPropertyStringPreprocess(compiler);
}
};
/** Creates a typed scope and adds types to the type registry. */
final HotSwapPassFactory resolveTypes =
new HotSwapPassFactory("resolveTypes", false) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new GlobalTypeResolver(compiler);
}
};
/** Runs type inference. */
final HotSwapPassFactory inferTypes =
new HotSwap
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>PassFactory("inferTypes", false) {
@Override
protected HotSwapCompilerPass createInternal(final AbstractCompiler
compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(getTypedScopeCreator());
makeTypeInference(compiler).process(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
makeTypeInference(compiler).inferTypes(scriptRoot);
}
};
}
};
final HotSwapPassFactory inferJsDocInfo =
new HotSwapPassFactory("inferJsDocInfo", false) {
@Override
protected HotSwapCompilerPass createInternal(
final AbstractCompiler compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(getTypedScopeCreator());
makeInferJsDocInfo(compiler).process(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
makeInferJsDocInfo(compiler).hotSwapScript(scriptRoot, originalRoot);
}
};
}
};
/** Checks type usage */
final HotSwapPassFactory checkTypes =
new HotSwapPassFactory("checkTypes", false) {
@Override
protected HotSwapCompilerPass createInternal(final AbstractCompiler
compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(getTypedScopeCreator());
TypeCheck check = makeTypeCheck(compiler);
check.process(externs, root);
compiler.getErrorManager().setTypedPercent(check.getTypedPercent());
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
makeTypeCheck(compiler).check(scriptRoot, false);
}
};
}
};
/**
* Checks possible execution paths of the program for problems: missing return
* statements and dead code.
*/
final HotSwapPassFactory checkControlFlow =
new HotSwapPassFactory("checkControlFlow", true) {
@Override
protected HotSwapCompilerPass createInternal
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>(AbstractCompiler compiler) {
List<Callback> callbacks = Lists.newArrayList();
if (options.checkUnreachableCode.isOn()) {
callbacks.add(
new CheckUnreachableCode(compiler, options.checkUnreachableCode));
}
if (options.checkMissingReturn.isOn() && options.checkTypes) {
callbacks.add(
new CheckMissingReturn(compiler, options.checkMissingReturn));
}
return combineChecks(compiler, callbacks);
}
};
/** Checks access controls. Depends on type-inference. */
final HotSwapPassFactory checkAccessControls =
new HotSwapPassFactory("checkAccessControls", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new CheckAccessControls(compiler);
}
};
/** Executes the given callbacks with a {@link CombinedCompilerPass}. */
private static HotSwapCompilerPass combineChecks(AbstractCompiler compiler,
List<Callback> callbacks) {
Preconditions.checkArgument(callbacks.size() > 0);
Callback[] array = callbacks.toArray(new Callback[callbacks.size()]);
return new CombinedCompilerPass(compiler, array);
}
/** A compiler pass that resolves types in the global scope. */
class GlobalTypeResolver implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
GlobalTypeResolver(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
if (topScope == null) {
regenerateGlobalTypedScope(compiler, root.getParent());
} else {
compiler.getTypeRegistry().resolveTypesInScope(topScope);
}
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
patchGlobalTypedScope(compiler, scriptRoot);
}
}
/** Checks global name usage. */
final PassFactory checkGlobalNames =
new PassFactory("checkGlobalNames", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
// Create a global namespace for analysis by check passes.
// Note that this class does all heavy computation lazily,
// so it's OK to create it here.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
process(null, null);
}
};
}
};
/** Checks that all constants are not modified */
final PassFactory checkConsts =
new PassFactory("checkConsts", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new ConstCheck(compiler);
}
};
/** Computes the names of functions for later analysis. */
final PassFactory computeFunctionNames =
new PassFactory("computeFunctionNames", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return ((functionNames = new FunctionNames(compiler)));
}
};
/** Skips Caja-private properties in for-in loops */
final PassFactory ignoreCajaProperties =
new PassFactory("ignoreCajaProperties", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new IgnoreCajaProperties(compiler);
}
};
/** Inserts runtime type assertions for debugging. */
final PassFactory runtimeTypeCheck =
new PassFactory("runtimeTypeCheck", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new RuntimeTypeCheck(compiler,
options.runtimeTypeCheckLogFunction);
}
};
/** Generates unique ids. */
final PassFactory replaceIdGenerators =
new PassFactory("replaceIdGenerators", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
ReplaceIdGenerators pass =
new ReplaceIdGenerators(compiler, options.idGenerators);
pass.process(externs, root);
idGeneratorMap = pass.getIdGeneratorMap();
}
};
}
};
/** Replace strings. */
final PassFactory replaceStrings =
new PassFactory("replaceStrings", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
ReplaceStrings pass = new ReplaceStrings(
compiler,
options.replaceStringsPlaceholderToken,
options.replaceStringsFunctionDescriptions,
options.replaceStringsReservedStrings);
pass.process(externs, root);
stringMap
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> };
/**
* Use data flow analysis to remove dead branches.
*/
final PassFactory removeUnreachableCode =
new PassFactory("removeUnreachableCode", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new UnreachableCodeElimination(compiler, true);
}
};
/**
* Remove prototype properties that do not appear to be used.
*/
final PassFactory removeUnusedPrototypeProperties =
new PassFactory("removeUnusedPrototypeProperties", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new RemoveUnusedPrototypeProperties(
compiler, options.removeUnusedPrototypePropertiesInExterns,
!options.removeUnusedVars);
}
};
/**
* Remove prototype properties that do not appear to be used.
*/
final PassFactory removeUnusedClassProperties =
new PassFactory("removeUnusedClassProperties", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new RemoveUnusedClassProperties(compiler);
}
};
/**
* Process smart name processing - removes unused classes and does referencing
* starting with minimum set of names.
*/
final PassFactory smartNamePass =
new PassFactory("smartNamePass", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
NameAnalyzer na = new NameAnalyzer(compiler, false);
na.process(externs, root);
String reportPath = options.reportPath;
if (reportPath != null) {
try {
Files.write(na.getHtmlReport(), new File(reportPath),
Charsets.UTF_8);
} catch (IOException e) {
compiler.report(JSError.make(REPORT_PATH_IO_ERROR, reportPath));
}
}
if (options.smartNameRemoval) {
na.removeUnreferenced();
}
}
};
}
};
/**
* Process smart name processing - removes unused classes and does referencing
* starting with minimum set of names.
*/
final PassFactory smartNamePass2 =
new PassFactory("smartNamePass", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>() {
@Override
public void process(Node externs, Node root) {
NameAnalyzer na = new NameAnalyzer(compiler, false);
na.process(externs, root);
na.removeUnreferenced();
}
};
}
};
/** Inlines simple methods, like getters */
final PassFactory inlineSimpleMethods =
new PassFactory("inlineSimpleMethods", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new InlineSimpleMethods(compiler);
}
};
/** Kills dead assignments. */
final PassFactory deadAssignmentsElimination =
new PassFactory("deadAssignmentsElimination", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new DeadAssignmentsElimination(compiler);
}
};
/** Inlines function calls. */
final PassFactory inlineFunctions =
new PassFactory("inlineFunctions", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
boolean enableBlockInlining = !isInliningForbidden();
return new InlineFunctions(
compiler,
compiler.getUniqueNameIdSupplier(),
options.inlineFunctions,
options.inlineLocalFunctions,
enableBlockInlining,
options.assumeStrictThis()
|| options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT,
options.assumeClosuresOnlyCaptureReferences);
}
};
/** Removes variables that are never used. */
final PassFactory removeUnusedVars =
new PassFactory("removeUnusedVars", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
boolean removeOnlyLocals = options.removeUnusedLocalVars
&& !options.removeUnusedVars;
boolean preserveAnonymousFunctionNames =
options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF;
return new RemoveUnusedVars(
compiler,
!removeOnlyLocals,
preserveAnonymousFunctionNames,
false);
}
};
/**
* Move global symbols to a deeper common module
*/
final PassFactory crossModuleCodeMotion =
new PassFactory("crossModuleCodeMotion", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new CrossModuleCodeMotion(compiler, compiler.getModuleGraph());
}
};
/**
* Move methods to a deeper common module
*/
final PassFactory
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>groupVariableDeclarations", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new GroupVariableDeclarations(compiler);
}
};
/**
* Extracts common sub-expressions.
*/
final PassFactory extractPrototypeMemberDeclarations =
new PassFactory("extractPrototypeMemberDeclarations", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new ExtractPrototypeMemberDeclarations(
compiler, Pattern.USE_GLOBAL_TEMP);
}
};
/** Rewrites common function definitions to be more compact. */
final PassFactory rewriteFunctionExpressions =
new PassFactory("rewriteFunctionExpressions", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new FunctionRewriter(compiler);
}
};
/** Collapses functions to not use the VAR keyword. */
final PassFactory collapseAnonymousFunctions =
new PassFactory("collapseAnonymousFunctions", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new CollapseAnonymousFunctions(compiler);
}
};
/** Moves function declarations to the top, to simulate actual hoisting. */
final PassFactory moveFunctionDeclarations =
new PassFactory("moveFunctionDeclarations", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new MoveFunctionDeclarations(compiler);
}
};
final PassFactory nameUnmappedAnonymousFunctions =
new PassFactory("nameAnonymousFunctions", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new NameAnonymousFunctions(compiler);
}
};
final PassFactory nameMappedAnonymousFunctions =
new PassFactory("nameAnonymousFunctions", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
NameAnonymousFunctionsMapped naf =
new NameAnonymousFunctionsMapped(compiler);
naf.process(externs, root);
anonymousFunctionNameMap = naf.getFunctionMap();
}
};
}
};
/** Alias external symbols. */
final PassFactory aliasExternals =
new PassFactory("aliasExternals", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new AliasExternals(compiler, compiler.getModuleGraph
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>(),
options.unaliasableGlobals, options.aliasableGlobals);
}
};
/**
* Alias string literals with global variables, to avoid creating lots of
* transient objects.
*/
final PassFactory aliasStrings =
new PassFactory("aliasStrings", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new AliasStrings(
compiler,
compiler.getModuleGraph(),
options.aliasAllStrings ? null : options.aliasableStrings,
options.aliasStringsBlacklist,
options.outputJsStringUsage);
}
};
/** Aliases common keywords (true, false) */
final PassFactory aliasKeywords =
new PassFactory("aliasKeywords", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new AliasKeywords(compiler);
}
};
/** Handling for the ObjectPropertyString primitive. */
final PassFactory objectPropertyStringPostprocess =
new PassFactory("ObjectPropertyStringPostprocess", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new ObjectPropertyStringPostprocess(compiler);
}
};
/**
* Renames properties so that the two properties that never appear on
* the same object get the same name.
*/
final PassFactory ambiguateProperties =
new PassFactory("ambiguateProperties", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new AmbiguateProperties(
compiler, options.anonymousFunctionNaming.getReservedCharacters());
}
};
/**
* Mark the point at which the normalized AST assumptions no longer hold.
*/
final PassFactory markUnnormalized =
new PassFactory("markUnnormalized", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
compiler.setLifeCycleStage(LifeCycleStage.RAW);
}
};
}
};
/** Denormalize the AST for code generation. */
final PassFactory denormalize =
new PassFactory("denormalize", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new Denormalize(compiler);
}
};
/** Inverting name normalization. */
final PassFactory invertContext
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>ualRenaming =
new PassFactory("invertNames", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return MakeDeclaredNamesUnique.getContextualRenameInverter(compiler);
}
};
/**
* Renames properties.
*/
final PassFactory renameProperties =
new PassFactory("renameProperties", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
VariableMap map = null;
if (options.inputPropertyMapSerialized != null) {
try {
map = VariableMap.fromBytes(options.inputPropertyMapSerialized);
} catch (ParseException e) {
return new ErrorPass(compiler,
JSError.make(INPUT_MAP_PROP_PARSE, e.getMessage()));
}
}
final VariableMap prevPropertyMap = map;
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
propertyMap = runPropertyRenaming(
compiler, prevPropertyMap, externs, root);
}
};
}
};
private VariableMap runPropertyRenaming(
AbstractCompiler compiler, VariableMap prevPropertyMap,
Node externs, Node root) {
char[] reservedChars =
options.anonymousFunctionNaming.getReservedCharacters();
switch (options.propertyRenaming) {
case HEURISTIC:
RenamePrototypes rproto = new RenamePrototypes(compiler, false,
reservedChars, prevPropertyMap);
rproto.process(externs, root);
return rproto.getPropertyMap();
case AGGRESSIVE_HEURISTIC:
RenamePrototypes rproto2 = new RenamePrototypes(compiler, true,
reservedChars, prevPropertyMap);
rproto2.process(externs, root);
return rproto2.getPropertyMap();
case ALL_UNQUOTED:
RenameProperties rprop = new RenameProperties(
compiler, options.propertyAffinity, options.generatePseudoNames,
prevPropertyMap, reservedChars);
rprop.process(externs, root);
return rprop.getPropertyMap();
default:
throw new IllegalStateException(
"Unrecognized property renaming policy");
}
}
/** Renames variables. */
final PassFactory renameVars =
new PassFactory("renameVars", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> compiler) {
VariableMap map = null;
if (options.inputVariableMapSerialized != null) {
try {
map = VariableMap.fromBytes(options.inputVariableMapSerialized);
} catch (ParseException e) {
return new ErrorPass(compiler,
JSError.make(INPUT_MAP_VAR_PARSE, e.getMessage()));
}
}
final VariableMap prevVariableMap = map;
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
variableMap = runVariableRenaming(
compiler, prevVariableMap, externs, root);
}
};
}
};
private VariableMap runVariableRenaming(
AbstractCompiler compiler, VariableMap prevVariableMap,
Node externs, Node root) {
char[] reservedChars =
options.anonymousFunctionNaming.getReservedCharacters();
boolean preserveAnonymousFunctionNames =
options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF;
RenameVars rn = new RenameVars(
compiler,
options.renamePrefix,
options.variableRenaming == VariableRenamingPolicy.LOCAL,
preserveAnonymousFunctionNames,
options.generatePseudoNames,
options.shadowVariables,
prevVariableMap,
reservedChars,
exportedNames);
rn.process(externs, root);
return rn.getVariableMap();
}
/** Renames labels */
final PassFactory renameLabels =
new PassFactory("renameLabels", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new RenameLabels(compiler);
}
};
/** Convert bracket access to dot access */
final PassFactory convertToDottedProperties =
new PassFactory("convertToDottedProperties", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new ConvertToDottedProperties(compiler);
}
};
/** Checks that all variables are defined. */
final PassFactory sanityCheckAst =
new PassFactory("sanityCheckAst", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new AstValidator();
}
};
/** Checks that all variables are defined. */
final PassFactory sanityCheckVars =
new PassFactory("sanityCheckVars", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> new VarCheck(compiler, true);
}
};
/** Adds instrumentations according to an instrumentation template. */
final PassFactory instrumentFunctions =
new PassFactory("instrumentFunctions", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
try {
FileReader templateFile =
new FileReader(options.instrumentationTemplate);
(new InstrumentFunctions(
compiler, functionNames,
options.instrumentationTemplate,
options.appNameStr,
templateFile)).process(externs, root);
} catch (IOException e) {
compiler.report(
JSError.make(AbstractCompiler.READ_ERROR,
options.instrumentationTemplate));
}
}
};
}
};
/**
* Create a no-op pass that can only run once. Used to break up loops.
*/
static PassFactory createEmptyPass(String name) {
return new PassFactory(name, true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return runInSerial();
}
};
}
/**
* Runs custom passes that are designated to run at a particular time.
*/
private PassFactory getCustomPasses(
final CustomPassExecutionTime executionTime) {
return new PassFactory("runCustomPasses", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return runInSerial(options.customPasses.get(executionTime));
}
};
}
/**
* All inlining is forbidden in heuristic renaming mode, because inlining
* will ruin the invariants that it depends on.
*/
private boolean isInliningForbidden() {
return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC ||
options.propertyRenaming ==
PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC;
}
/** Create a compiler pass that runs the given passes in serial. */
private static CompilerPass runInSerial(final CompilerPass ... passes) {
return runInSerial(Lists.newArrayList(passes));
}
/** Create a compiler pass that runs the given passes in serial. */
private static CompilerPass runInSerial(
final Collection<CompilerPass> passes) {
return new CompilerPass() {
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
@Override public void process(Node externs, Node root) {
for (CompilerPass pass : passes) {
pass.process(externs, root);
}
}
};
}
@VisibleForTesting
static Map<String, Node> getAdditionalReplacements(
CompilerOptions options) {
Map<String, Node> additionalReplacements = Maps.newHashMap();
if (options.markAsCompiled || options.closurePass) {
additionalReplacements.put(COMPILED_CONSTANT_NAME, IR.trueNode());
}
if (options.closurePass && options.locale != null) {
additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME,
IR.string(options.locale));
}
return additionalReplacements;
}
final PassFactory printNameReferenceGraph =
new PassFactory("printNameReferenceGraph", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
NameReferenceGraphConstruction gc =
new NameReferenceGraphConstruction(compiler);
gc.process(externs, jsRoot);
String graphFileName = options.nameReferenceGraphPath;
try {
Files.write(DotFormatter.toDot(gc.getNameReferenceGraph()),
new File(graphFileName),
Charsets.UTF_8);
} catch (IOException e) {
compiler.report(
JSError.make(
NAME_REF_GRAPH_FILE_ERROR, e.getMessage(), graphFileName));
}
}
};
}
};
final PassFactory printNameReferenceReport =
new PassFactory("printNameReferenceReport", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
NameReferenceGraphConstruction gc =
new NameReferenceGraphConstruction(compiler);
String reportFileName = options.nameReferenceReportPath;
try {
NameReferenceGraphReport report =
new NameReferenceGraphReport(gc.getNameReferenceGraph());
Files.write(report.getHtmlReport(),
new File(reportFileName),
Charsets.UTF_8);
} catch (IOException e) {
compiler.report(
JSError.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* A builder for the Rhino Node representing Function parameters.
* @author nicksantos@google.com (Nick Santos)
*/
public class FunctionParamBuilder {
private final JSTypeRegistry registry;
private final Node root = new Node(Token.PARAM_LIST);
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>String(Token.NAME, "");
paramNode.setJSType(type);
root.addChildToBack(paramNode);
return paramNode;
}
public Node build() {
return root;
}
private boolean hasOptionalOrVarArgs() {
Node lastChild = root.getLastChild();
return lastChild != null &&
(lastChild.isOptionalArg() || lastChild.isVarArgs());
}
public boolean hasVarArgs() {
Node lastChild = root.getLastChild();
return lastChild != null && lastChild.isVarArgs();
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
*/
public class ScriptRuntime {
/**
* No instances should be created.
*/
protected ScriptRuntime() {
}
// It is public so NativeRegExp can access it .
public static boolean isJSLineTerminator(int c) {
// Optimization for faster check for eol character:
// they do not have 0xDFD0 bits set
if ((c & 0xDFD0) != 0) {
return false;
}
return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
}
// Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM,
// versions 2.01 and 3.0P1, that causes some uses (returns at least) of
// Double.NaN to be converted to 1.0.
// So we use ScriptRuntime.NaN instead of Double.NaN.
public static final double
NaN = Double.longBitsToDouble(0x7ff8000000000000L);
// A similar problem exists for negative zero.
public static final double
negativeZero = Double.longBitsToDouble(0x8000000000000000L);
/*
* Helper function for toNumber, parseInt, and TokenStream.getToken.
*/
@SuppressWarnings("fallthrough")
static double stringToNumber(String s, int start, int radix) {
char digitMax = '9';
char lowerCaseBound = 'a';
char upperCaseBound = 'A';
int len = s.length();
if (radix < 10) {
digitMax = (char) ('0' + radix - 1);
}
if (radix > 10) {
lowerCaseBound = (char) ('a' + radix - 10);
upperCaseBound = (char) ('A' + radix - 10);
}
int end;
double sum = 0.0;
for (end=start; end < len; end++) {
char c = s.charAt(end);
int newDigit;
if ('0' <= c && c <= digitMax)
newDigit
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> = c - '0';
else if ('a' <= c && c < lowerCaseBound)
newDigit = c - 'a' + 10;
else if ('A' <= c && c < upperCaseBound)
newDigit = c - 'A' + 10;
else
break;
sum = sum*radix + newDigit;
}
if (start == end) {
return NaN;
}
if (sum >= 9007199254740992.0) {
if (radix == 10) {
/* If we're accumulating a decimal number and the number
* is >= 2^53, then the result from the repeated multiply-add
* above may be inaccurate. Call Java to get the correct
* answer.
*/
try {
return Double.valueOf(s.substring(start, end)).doubleValue();
} catch (NumberFormatException nfe) {
return NaN;
}
} else if (radix == 2 || radix == 4 || radix == 8 ||
radix == 16 || radix == 32) {
/* The number may also be inaccurate for one of these bases.
* This happens if the addition in value*radix + digit causes
* a round-down to an even least significant mantissa bit
* when the first dropped bit is a one. If any of the
* following digits in the number (which haven't been added
* in yet) are nonzero then the correct action would have
* been to round up instead of down. An example of this
* occurs when reading the number 0x1000000000000081, which
* rounds to 0x1000000000000000 instead of 0x1000000000000100.
*/
int bitShiftInChar = 1;
int digit = 0;
final int SKIP_LEADING_ZEROS = 0;
final int FIRST_EXACT_53_BITS = 1;
final int AFTER_BIT_53 = 2;
final int ZEROS_AFTER_54 = 3;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
final int MIXED_AFTER_54 = 4;
int state = SKIP_LEADING_ZEROS;
int exactBitsLimit = 53;
double factor = 0.0;
boolean bit53 = false;
// bit54 is the 54th bit (the first dropped from the mantissa)
boolean bit54 = false;
for (;;) {
if (bitShiftInChar == 1) {
if (start == end)
break;
digit = s.charAt(start++);
if ('0' <= digit && digit <= '9')
digit -= '0';
else if ('a' <= digit && digit <= 'z')
digit -= 'a' - 10;
else
digit -= 'A' - 10;
bitShiftInChar = radix;
}
bitShiftInChar >>= 1;
boolean bit = (digit & bitShiftInChar) != 0;
switch (state) {
case SKIP_LEADING_ZEROS:
if (bit) {
--exactBitsLimit;
sum = 1.0;
state = FIRST_EXACT_53_BITS;
}
break;
case FIRST_EXACT_53_BITS:
sum *= 2.0;
if (bit)
sum += 1.0;
--exactBitsLimit;
if (exactBitsLimit == 0) {
bit53 = bit;
state = AFTER_BIT_53;
}
break;
case AFTER_BIT_53:
bit54 = bit;
factor = 2.0;
state = ZEROS_AFTER_54;
break;
case ZEROS_AFTER_54:
if (bit) {
state = MIXED_AFTER_54;
}
// fallthrough
case MIXED_AFTER_54:
factor *= 2;
break;
}
}
switch (state) {
case SKIP_LEADING_ZEROS:
sum = 0.0;
break;
case FIRST_EXACT_53_BITS:
case AFTER_BIT_53:
// do nothing
break;
case ZEROS_AFTER_54:
// x1.1 -> x
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>1 + 1 (round up)
// x0.1 -> x0 (round down)
if (bit54 & bit53)
sum += 1.0;
sum *= factor;
break;
case MIXED_AFTER_54:
// x.100...1.. -> x + 1 (round up)
// x.0anything -> x (round down)
if (bit54)
sum += 1.0;
sum *= factor;
break;
}
}
/* We don't worry about inaccurate numbers for any other base. */
}
return sum;
}
public static String escapeString(String s) {
return escapeString(s, '"');
}
/**
* For escaping strings printed by object and array literals; not quite
* the same as 'escape.'
*/
public static String escapeString(String s, char escapeQuote) {
if (!(escapeQuote == '"' || escapeQuote == '\'')) {
throw new IllegalStateException("unexpected quote char:" + escapeQuote);
}
StringBuffer sb = null;
for(int i = 0, L = s.length(); i != L; ++i) {
int c = s.charAt(i);
if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') {
// an ordinary print character (like C isprint()) and not "
// or \ .
if (sb != null) {
sb.append((char)c);
}
continue;
}
if (sb == null) {
sb = new StringBuffer(L + 3);
sb.append(s);
sb.setLength(i);
}
int escape = -1;
switch (c) {
case '\b': escape = 'b'; break;
case '\f': escape = 'f'; break;
case '\n': escape = 'n'; break;
case '\r': escape = 'r'; break;
case '\t': escape = 't'; break;
case 0xb: escape = 'v'; break; // Java lacks \v.
case ' ': escape = ' '; break;
case '\\': escape = '\\'; break;
}
if (
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>escape >= 0) {
// an \escaped sort of character
sb.append('\\');
sb.append((char)escape);
} else if (c == escapeQuote) {
sb.append('\\');
sb.append(escapeQuote);
} else {
int hexSize;
if (c < 256) {
// 2-digit hex
sb.append("\\x");
hexSize = 2;
} else {
// Unicode.
sb.append("\\u");
hexSize = 4;
}
// append hexadecimal form of c left-padded with 0
for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
int digit = 0xf & (c >> shift);
int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit;
sb.append((char)hc);
}
}
}
return (sb == null) ? s : sb.toString();
}
static boolean isValidIdentifierName(String s) {
int L = s.length();
if (L == 0)
return false;
if (!Character.isJavaIdentifierStart(s.charAt(0)))
return false;
for (int i = 1; i != L; ++i) {
if (!Character.isJavaIdentifierPart(s.charAt(i)))
return false;
}
return !TokenStream.isKeyword(s);
}
/**
* If str is a decimal presentation of Uint32 value, return it as long.
* Othewise return -1L;
*/
public static long testUint32String(String str) {
// The length of the decimal string representation of
// UINT32_MAX_VALUE, 4294967296
final int MAX_VALUE_LENGTH = 10;
int len = str.length();
if (1 <= len && len <= MAX_VALUE_LENGTH) {
int c = str.charAt(0);
c -= '0';
if (c == 0) {
// Note that 00,01 etc. are not valid Uint32 presentations
return (len == 1) ? 0L :
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> -1L;
}
if (1 <= c && c <= 9) {
long v = c;
for (int i = 1; i != len; ++i) {
c = str.charAt(i) - '0';
if (!(0 <= c && c <= 9)) {
return -1;
}
v = 10 * v + c;
}
// Check for overflow
if ((v >>> 32) == 0) {
return v;
}
}
}
return -1;
}
static boolean isSpecialProperty(String s) {
return s.equals("__proto__") || s.equals("__parent__");
}
// ------------------
// Statements
// ------------------
public static String getMessage0(String messageId) {
return getMessage(messageId, null);
}
public static String getMessage1(String messageId, Object arg1) {
Object[] arguments = {arg1};
return getMessage(messageId, arguments);
}
/* OPT there's a noticable delay for the first error! Maybe it'd
* make sense to use a ListResourceBundle instead of a properties
* file to avoid (synchronized) text parsing.
*/
public static String getMessage(String messageId, Object[] arguments) {
final String defaultResource
= "rhino_ast.java.com.google.javascript.rhino.Messages";
Locale locale = Locale.getDefault();
// ResourceBundle does cacheing.
ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale);
String formatString;
try {
formatString = rb.getString(messageId);
} catch (java.util.MissingResourceException mre) {
throw new RuntimeException
("no message resource found for message property "+ messageId);
}
/*
* It's OK to format the string, even if 'arguments' is null;
* we need to format it anyway, to make double ''s collapse to
* single 's.
*/
// TODO: MessageFormat is not available on pJava
MessageFormat formatter = new MessageFormat(formatString);
return formatter.format(arguments);
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2006 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.Node;
/**
* <p>Interface for classes that can compile JS.</p>
*
* <p>Class has single function "process", which is passed
* the root node of the parsed JS tree, as well as the
* root node of the external JS tree (used to provide a public API
* and prevent renaming of system functions).</p>
*
* <p>Use this class to support testing with BaseCompilerTest</p>
*
*/
public interface CompilerPass {
/**
* Process the JS with root node root.
* Can modify the contents of each Node tree
* @param externs Top of external JS tree
* @param root Top of JS tree
*/
void process(Node externs, Node root);
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> fileName, InputStream s)
throws IOException {
return new JSSourceFile(SourceFile.fromInputStream(fileName, s));
}
public static JSSourceFile fromGenerator(String fileName,
Generator generator) {
return new JSSourceFile(SourceFile.fromGenerator(fileName, generator));
}
private SourceFile referenced;
private JSSourceFile(SourceFile referenced) {
super(referenced.getName());
this.referenced = referenced;
}
@Override
public String getCode() throws IOException {
return referenced.getCode();
}
@Override
public void clearCachedSource() {
referenced.clearCachedSource();
}
@Override
@VisibleForTesting
String getCodeNoCache() {
return referenced.getCodeNoCache();
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> public CompilerInput(SourceAst ast) {
this(ast, ast.getSourceFile().getName(), false);
}
public CompilerInput(SourceAst ast, boolean isExtern) {
this(ast, ast.getInputId(), isExtern);
}
public CompilerInput(SourceAst ast, String inputId, boolean isExtern) {
this(ast, new InputId(inputId), isExtern);
}
public CompilerInput(SourceAst ast, InputId inputId, boolean isExtern) {
this.ast = ast;
this.id = inputId;
// TODO(nicksantos): Add a precondition check here. People are passing
// in null, but they should not be.
if (ast != null && ast.getSourceFile() != null) {
ast.getSourceFile().setIsExtern(isExtern);
}
}
public CompilerInput(JSSourceFile file) {
this(file, false);
}
public CompilerInput(JSSourceFile file, boolean isExtern) {
this(new JsAst(file), isExtern);
}
/** Returns a name for this input. Must be unique across all inputs. */
@Override
public InputId getInputId() {
return id;
}
/** Returns a name for this input. Must be unique across all inputs. */
@Override
public String getName() {
return id.getIdName();
}
public SourceAst getAst() {
return ast;
}
/** Gets the path relative to closure-base, if one is available. */
@Override
public String getPathRelativeToClosureBase() {
// TODO(nicksantos): Implement me.
throw new UnsupportedOperationException();
}
@Override
public Node getAstRoot(AbstractCompiler compiler) {
Node root = ast.getAstRoot(compiler);
// The root maybe null if the AST can not be created.
if (root != null) {
Preconditions.checkState(root.isScript());
Preconditions.checkNotNull(root.getInputId());
}
return root;
}
@Override
public void clearAst() {
ast.clearAst();
}
@Override
public SourceFile getSourceFile() {
return ast.getSourceFile();
}
@Override
public void setSourceFile(SourceFile file) {
ast
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.setSourceFile(file);
}
/** Returns the SourceAst object on which this input is based. */
public SourceAst getSourceAst() {
return ast;
}
/** Sets an error manager for routing error messages. */
public void setErrorManager(ErrorManager errorManager) {
this.errorManager = errorManager;
}
/** Sets an abstract compiler for doing parsing. */
public void setCompiler(AbstractCompiler compiler) {
this.compiler = compiler;
setErrorManager(compiler.getErrorManager());
}
/** Gets a list of types depended on by this input. */
@Override
public Collection<String> getRequires() {
Preconditions.checkNotNull(errorManager,
"Expected setErrorManager to be called first");
try {
regenerateDependencyInfoIfNecessary();
return Collections.<String>unmodifiableSet(requires);
} catch (IOException e) {
errorManager.report(CheckLevel.ERROR,
JSError.make(AbstractCompiler.READ_ERROR, getName()));
return ImmutableList.<String>of();
}
}
/** Gets a list of types provided by this input. */
@Override
public Collection<String> getProvides() {
Preconditions.checkNotNull(errorManager,
"Expected setErrorManager to be called first");
try {
regenerateDependencyInfoIfNecessary();
return Collections.<String>unmodifiableSet(provides);
} catch (IOException e) {
errorManager.report(CheckLevel.ERROR,
JSError.make(AbstractCompiler.READ_ERROR, getName()));
return ImmutableList.<String>of();
}
}
/**
* Regenerates the provides/requires if we need to do so.
*/
private void regenerateDependencyInfoIfNecessary() throws IOException {
// If the code is NOT a JsAst, then it was not originally JS code.
// Look at the Ast for dependency info.
if (!(ast instanceof JsAst)) {
Preconditions.checkNotNull(compiler,
"Expected setCompiler to be called first");
DepsFinder finder = new DepsFinder();
Node root = getAstRoot(compiler);
if (root == null) {
return;
}
finder.visitTree(getAstRoot(compiler));
// TODO(nicksantos|user): This caching behavior is a bit
// odd, and only works if you assume the exact call flow that
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> method effectively emulates the
* <code>Number()</code> JavaScript cast function.
*/
static Double getNumberValue(Node n) {
switch (n.getType()) {
case Token.TRUE:
return 1.0;
case Token.FALSE:
case Token.NULL:
return 0.0;
case Token.NUMBER:
return n.getDouble();
case Token.VOID:
if (mayHaveSideEffects(n.getFirstChild())) {
return null;
} else {
return Double.NaN;
}
case Token.NAME:
// Check for known constants
String name = n.getString();
if (name.equals("undefined")) {
return Double.NaN;
}
if (name.equals("NaN")) {
return Double.NaN;
}
if (name.equals("Infinity")) {
return Double.POSITIVE_INFINITY;
}
return null;
case Token.NEG:
if (n.getChildCount() == 1 && n.getFirstChild().isName()
&& n.getFirstChild().getString().equals("Infinity")) {
return Double.NEGATIVE_INFINITY;
}
return null;
case Token.NOT:
TernaryValue child = getPureBooleanValue(n.getFirstChild());
if (child != TernaryValue.UNKNOWN) {
return child.toBoolean(true) ? 0.0 : 1.0; // reversed.
}
break;
case Token.STRING:
return getStringNumberValue(n.getString());
case Token.ARRAYLIT:
case Token.OBJECTLIT:
String value = getStringValue(n);
return value != null ? getStringNumberValue(value) : null;
}
return null;
}
static Double getStringNumberValue(String rawJsString) {
if (rawJsString.contains("\u000b")) {
// vertical tab is not always whitespace
return null;
}
String s = trimJsWhiteSpace(rawJsString);
// return ScriptRuntime.toNumber(s);
if (s.length() == 0) {
return 0.0;
}
if (s.length() > 2
&& s.charAt(0) == '0'
&& (s.charAt(1) == 'x' || s.charAt(1) == 'X
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>')) {
// Attempt to convert hex numbers.
try {
return Double.valueOf(Integer.parseInt(s.substring(2), 16));
} catch (NumberFormatException e) {
return Double.NaN;
}
}
if (s.length() > 3
&& (s.charAt(0) == '-' || s.charAt(0) == '+')
&& s.charAt(1) == '0'
&& (s.charAt(2) == 'x' || s.charAt(2) == 'X')) {
// hex numbers with explicit signs vary between browsers.
return null;
}
// FireFox and IE treat the "Infinity" differently. FireFox is case
// insensitive, but IE treats "infinity" as NaN. So leave it alone.
if (s.equals("infinity")
|| s.equals("-infinity")
|| s.equals("+infinity")) {
return null;
}
try {
return Double.parseDouble(s);
} catch (NumberFormatException e) {
return Double.NaN;
}
}
static String trimJsWhiteSpace(String s) {
int start = 0;
int end = s.length();
while (end > 0
&& isStrWhiteSpaceChar(s.charAt(end - 1)) == TernaryValue.TRUE) {
end--;
}
while (start < end
&& isStrWhiteSpaceChar(s.charAt(start)) == TernaryValue.TRUE) {
start++;
}
return s.substring(start, end);
}
/**
* Copied from Rhino's ScriptRuntime
*/
static TernaryValue isStrWhiteSpaceChar(int c) {
switch (c) {
case '\u000B': // <VT>
return TernaryValue.UNKNOWN; // IE says "no", EcmaScript says "yes"
case ' ': // <SP>
case '\n': // <LF>
case '\r': // <CR>
case '\t': // <TAB>
case '\u00A0': // <NBSP>
case '\u000C': // <FF>
case '\u2028': // <LS>
case '\u2029': // <PS>
case '\uFEFF
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Value(child, includeFunctions)) {
return false;
}
}
return true;
case Token.OBJECTLIT:
// Return true only if all values are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child.getFirstChild(), includeFunctions)) {
return false;
}
}
return true;
case Token.FUNCTION:
return includeFunctions && !NodeUtil.isFunctionDeclaration(n);
default:
return isImmutableValue(n);
}
}
/**
* Determines whether the given value may be assigned to a define.
*
* @param val The value being assigned.
* @param defines The list of names of existing defines.
*/
static boolean isValidDefineValue(Node val, Set<String> defines) {
switch (val.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.TRUE:
case Token.FALSE:
return true;
// Binary operators are only valid if both children are valid.
case Token.ADD:
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GT:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.URSH:
return isValidDefineValue(val.getFirstChild(), defines)
&& isValidDefineValue(val.getLastChild(), defines);
// Uniary operators are valid if the child is valid.
case Token.NOT:
case Token.NEG:
case Token.POS:
return isValidDefineValue(val.getFirstChild(), defines);
// Names are valid if and only if they are defines themselves.
case Token.NAME:
case Token.GETPROP:
if (val.isQualifiedName()) {
return defines.contains(val.getQualifiedName());
}
}
return false;
}
/**
* Returns whether this a BLOCK node with no children.
*
* @param
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> "var x = 0;".
*
* @param n The node
* @return True if n is an L-value.
*/
static boolean isLValue(Node n) {
Preconditions.checkArgument(n.isName() || n.isGetProp() ||
n.isGetElem());
Node parent = n.getParent();
return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n)
|| (NodeUtil.isForIn(parent) && parent.getFirstChild() == n)
|| parent.isVar()
|| (parent.isFunction() && parent.getFirstChild() == n)
|| parent.isDec()
|| parent.isInc()
|| parent.isParamList()
|| parent.isCatch();
}
/**
* Determines whether a node represents an object literal key
* (e.g. key1 in {key1: value1, key2: value2}).
*
* @param node A node
* @param parent The node's parent
*/
static boolean isObjectLitKey(Node node, Node parent) {
switch (node.getType()) {
case Token.STRING:
return parent.isObjectLit();
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return true;
}
return false;
}
/**
* Get the name of an object literal key.
*
* @param key A node
*/
static String getObjectLitKeyName(Node key) {
switch (key.getType()) {
case Token.STRING:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return key.getString();
}
throw new IllegalStateException("Unexpected node type: " + key);
}
/**
* @param key A OBJECTLIT key node.
* @return The type expected when using the key.
*/
static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) {
if (valueType != null) {
switch (key.getType()) {
case Token.GETTER_DEF:
// GET must always return a function type.
if (valueType.isFunctionType()) {
FunctionType fntype = valueType.toMaybeFunctionType();
valueType = fntype.getReturnType();
} else {
return null;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
*/
static void redeclareVarsInsideBranch(Node branch) {
Collection<Node> vars = getVarsDeclaredInBranch(branch);
if (vars.isEmpty()) {
return;
}
Node parent = getAddingRoot(branch);
for (Node nameNode : vars) {
Node var = IR.var(
IR.name(nameNode.getString())
.srcref(nameNode))
.srcref(nameNode);
copyNameAnnotations(nameNode, var.getFirstChild());
parent.addChildToFront(var);
}
}
/**
* Copy any annotations that follow a named value.
* @param source
* @param destination
*/
static void copyNameAnnotations(Node source, Node destination) {
if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) {
destination.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
/**
* Gets a Node at the top of the current scope where we can add new var
* declarations as children.
*/
private static Node getAddingRoot(Node n) {
Node addingRoot = null;
Node ancestor = n;
while (null != (ancestor = ancestor.getParent())) {
int type = ancestor.getType();
if (type == Token.SCRIPT) {
addingRoot = ancestor;
break;
} else if (type == Token.FUNCTION) {
addingRoot = ancestor.getLastChild();
break;
}
}
// make sure that the adding root looks ok
Preconditions.checkState(addingRoot.isBlock() ||
addingRoot.isScript());
Preconditions.checkState(addingRoot.getFirstChild() == null ||
!addingRoot.getFirstChild().isScript());
return addingRoot;
}
/**
* Creates a node representing a qualified name.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @return A NAME or GETPROP node
*/
public static Node newQualifiedNameNode(
CodingConvention convention, String name) {
int endPos = name.indexOf('.');
if (endPos == -1) {
return newName(convention, name);
}
Node node = newName(convention, name.substring(0, endPos));
int startPos;
do {
startPos =
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> endPos + 1;
endPos = name.indexOf('.', startPos);
String part = (endPos == -1
? name.substring(startPos)
: name.substring(startPos, endPos));
Node propNode = IR.string(part);
if (convention.isConstantKey(part)) {
propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
node = IR.getprop(node, propNode);
} while (endPos != -1);
return node;
}
/**
* Creates a node representing a qualified name, copying over the source
* location information from the basis node and assigning the given original
* name to the node.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @param basisNode The node that represents the name as currently found in
* the AST.
* @param originalName The original name of the item being represented by the
* NAME node. Used for debugging information.
*
* @return A NAME or GETPROP node
*/
static Node newQualifiedNameNode(
CodingConvention convention, String name, Node basisNode,
String originalName) {
Node node = newQualifiedNameNode(convention, name);
setDebugInformation(node, basisNode, originalName);
return node;
}
/**
* Gets the root node of a qualified name. Must be either NAME or THIS.
*/
public static Node getRootOfQualifiedName(Node qName) {
for (Node current = qName; true;
current = current.getFirstChild()) {
if (current.isName() || current.isThis()) {
return current;
}
Preconditions.checkState(current.isGetProp());
}
}
/**
* Sets the debug information (source file info and orignal name)
* on the given node.
*
* @param node The node on which to set the debug information.
* @param basisNode The basis node from which to copy the source file info.
* @param originalName The original name of the node.
*/
static void setDebugInformation(Node node, Node basisNode,
String originalName) {
node.copyInformationFromForTree(basisNode);
node.putProp(Node.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>in set.
*/
static boolean isLatin(String s) {
char LARGEST_BASIC_LATIN = 0x7f;
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c > LARGEST_BASIC_LATIN) {
return false;
}
}
return true;
}
/**
* Determines whether the given name is a valid variable name.
*/
public static boolean isValidSimpleName(String name) {
return TokenStream.isJSIdentifier(name) &&
!TokenStream.isKeyword(name) &&
// no Unicode escaped characters - some browsers are less tolerant
// of Unicode characters that might be valid according to the
// language spec.
// Note that by this point, unicode escapes have been converted
// to UTF-16 characters, so we're only searching for character
// values, not escapes.
isLatin(name);
}
/**
* Determines whether the given name is a valid qualified name.
*/
// TODO(nicksantos): This should be moved into a "Language" API,
// so that the results are different for es5 and es3.
public static boolean isValidQualifiedName(String name) {
if (name.endsWith(".") || name.startsWith(".")) {
return false;
}
String[] parts = name.split("\\.");
for (String part : parts) {
if (!isValidSimpleName(part)) {
return false;
}
}
return true;
}
/**
* Determines whether the given name can appear on the right side of
* the dot operator. Many properties (like reserved words) cannot.
*/
static boolean isValidPropertyName(String name) {
return isValidSimpleName(name);
}
private static class VarCollector implements Visitor {
final Map<String, Node> vars = Maps.newLinkedHashMap();
@Override
public void visit(Node n) {
if (n.isName()) {
Node parent = n.getParent();
if (parent != null && parent.isVar()) {
String name = n.getString();
if (!vars.containsKey(name)) {
vars.put(name, n);
}
}
}
}
}
/**
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
* Retrieves vars declared in the current node tree, excluding descent scopes.
*/
public static Collection<Node> getVarsDeclaredInBranch(Node root) {
VarCollector collector = new VarCollector();
visitPreOrder(
root,
collector,
MATCH_NOT_FUNCTION);
return collector.vars.values();
}
/**
* @return {@code true} if the node an assignment to a prototype property of
* some constructor.
*/
static boolean isPrototypePropertyDeclaration(Node n) {
if (!isExprAssign(n)) {
return false;
}
return isPrototypeProperty(n.getFirstChild().getFirstChild());
}
static boolean isPrototypeProperty(Node n) {
String lhsString = n.getQualifiedName();
if (lhsString == null) {
return false;
}
int prototypeIdx = lhsString.indexOf(".prototype.");
return prototypeIdx != -1;
}
/**
* @return The class name part of a qualified prototype name.
*/
static Node getPrototypeClassName(Node qName) {
Node cur = qName;
while (cur.isGetProp()) {
if (cur.getLastChild().getString().equals("prototype")) {
return cur.getFirstChild();
} else {
cur = cur.getFirstChild();
}
}
return null;
}
/**
* @return The string property name part of a qualified prototype name.
*/
static String getPrototypePropertyName(Node qName) {
String qNameStr = qName.getQualifiedName();
int prototypeIdx = qNameStr.lastIndexOf(".prototype.");
int memberIndex = prototypeIdx + ".prototype".length() + 1;
return qNameStr.substring(memberIndex);
}
/**
* Create a node for an empty result expression:
* "void 0"
*/
static Node newUndefinedNode(Node srcReferenceNode) {
Node node = IR.voidNode(IR.number(0));
if (srcReferenceNode != null) {
node.copyInformationFromForTree(srcReferenceNode);
}
return node;
}
/**
* Create a VAR node containing the given name and initial value expression.
*/
static Node newVarNode(String name, Node value) {
Node nodeName = IR.name(name);
if (value !=
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>li>The normalize pass renames any variable with the IS_CONSTANT_NAME
* annotation and that is initialized to a constant value with
* a variable name inlucding $$constant.
* <li>Return true here if the variable includes $$constant in its name.
* </ol>
*
* @param node A NAME or STRING node
* @return True if the variable is constant
*/
static boolean isConstantName(Node node) {
return node.getBooleanProp(Node.IS_CONSTANT_NAME);
}
/** Whether the given name is constant by coding convention. */
static boolean isConstantByConvention(
CodingConvention convention, Node node, Node parent) {
String name = node.getString();
if (parent.isGetProp() &&
node == parent.getLastChild()) {
return convention.isConstantKey(name);
} else if (isObjectLitKey(node, parent)) {
return convention.isConstantKey(name);
} else {
return convention.isConstant(name);
}
}
/**
* Get the JSDocInfo for a function.
*/
public static JSDocInfo getFunctionJSDocInfo(Node n) {
Preconditions.checkState(n.isFunction());
JSDocInfo fnInfo = n.getJSDocInfo();
if (fnInfo == null && NodeUtil.isFunctionExpression(n)) {
// Look for the info on other nodes.
Node parent = n.getParent();
if (parent.isAssign()) {
// on ASSIGNs
fnInfo = parent.getJSDocInfo();
} else if (parent.isName()) {
// on var NAME = function() { ... };
fnInfo = parent.getParent().getJSDocInfo();
}
}
return fnInfo;
}
/**
* @param n The node.
* @return The source name property on the node or its ancestors.
*/
public static String getSourceName(Node n) {
String sourceName = null;
while (sourceName == null && n != null) {
sourceName = n.getSourceFileName();
n = n.getParent();
}
return sourceName;
}
/**
* @param n The node.
* @return The source name property on the node or its ancestors.
*/
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> the nth
* argument or null if no such parameter exists.
*/
static Node getArgumentForFunction(Node function, int index) {
Preconditions.checkState(function.isFunction());
return getNthSibling(
function.getFirstChild().getNext().getFirstChild(), index);
}
/**
* Given the new or call, this returns the nth
* argument of the call or null if no such argument exists.
*/
static Node getArgumentForCallOrNew(Node call, int index) {
Preconditions.checkState(isCallOrNew(call));
return getNthSibling(
call.getFirstChild().getNext(), index);
}
private static boolean isToStringMethodCall(Node call) {
Node getNode = call.getFirstChild();
if (isGet(getNode)) {
Node propNode = getNode.getLastChild();
return propNode.isString() && "toString".equals(propNode.getString());
}
return false;
}
/** Find the best JSDoc for the given node. */
static JSDocInfo getBestJSDocInfo(Node n) {
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
Node parent = n.getParent();
if (parent == null) {
return null;
}
if (parent.isName()) {
return getBestJSDocInfo(parent);
} else if (parent.isAssign()) {
info = parent.getJSDocInfo();
} else if (isObjectLitKey(parent, parent.getParent())) {
info = parent.getJSDocInfo();
} else if (parent.isFunction()) {
info = parent.getJSDocInfo();
} else if (parent.isVar() && parent.hasOneChild()) {
info = parent.getJSDocInfo();
}
}
return info;
}
/** Find the l-value that the given r-value is being assigned to. */
static Node getBestLValue(Node n) {
Node parent = n.getParent();
boolean isFunctionDeclaration = isFunctionDeclaration(n);
if (isFunctionDeclaration) {
return n.getFirstChild();
} else if (parent.isName()) {
return parent;
} else if (parent.isAssign()) {
return parent.getFirstChild();
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
} else if (isObjectLitKey(parent, parent.getParent())) {
return parent;
}
return null;
}
/** Get the owner of the given l-value node. */
static Node getBestLValueOwner(@Nullable Node lValue) {
if (lValue == null || lValue.getParent() == null) {
return null;
}
if (isObjectLitKey(lValue, lValue.getParent())) {
return getBestLValue(lValue.getParent());
} else if (isGet(lValue)) {
return lValue.getFirstChild();
}
return null;
}
/** Get the name of the given l-value node. */
static String getBestLValueName(@Nullable Node lValue) {
if (lValue == null || lValue.getParent() == null) {
return null;
}
if (isObjectLitKey(lValue, lValue.getParent())) {
Node owner = getBestLValue(lValue.getParent());
if (owner != null) {
String ownerName = getBestLValueName(owner);
if (ownerName != null) {
return ownerName + "." + getObjectLitKeyName(lValue);
}
}
return null;
}
return lValue.getQualifiedName();
}
/**
* @returns false iff the result of the expression is not consumed.
*/
static boolean isExpressionResultUsed(Node expr) {
// TODO(johnlenz): consider sharing some code with trySimpleUnusedResult.
Node parent = expr.getParent();
switch (parent.getType()) {
case Token.EXPR_RESULT:
return false;
case Token.HOOK:
case Token.AND:
case Token.OR:
return (expr == parent.getFirstChild())
? true : isExpressionResultUsed(parent);
case Token.COMMA:
return (expr == parent.getFirstChild())
? false : isExpressionResultUsed(parent);
case Token.FOR:
if (!NodeUtil.isForIn(parent)) {
// Only an expression whose result is in the condition part of the
// expression is used.
return (parent.getChildAtIndex(1) == expr);
}
break;
}
return true;
}
static Node booleanNode(boolean value) {
return value ? IR.trueNode()
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.stop() is missing "
+ "or Tracer.stop() is not wrapped in a "
+ "try/finally block. "
+ "Clearing to avoid memory leak.",
new Throwable(trace.toString()));
trace.truncateOutstandingEvents();
}
trace.startEvent(this);
}
/**
* Create a tracer that isn't summed as part of a total
*
* @param comment Comment about this tracer
*/
Tracer(String comment) {
this(null, comment);
}
/**
* Construct a tracer whose type is based on the short name of the object
* @param object Object to use as type name
* @param comment A comment
* @return new Tracer.
*/
static Tracer shortName(Object object, String comment) {
if (object == null) {
return new Tracer(comment);
}
return new Tracer(object.getClass().getSimpleName(), comment);
}
/**
* Converts 'v' to a string and pads it with up to 16 spaces for
* improved alignment.
* @param v The value to convert.
* @param digits_column_width The desired with of the string.
*/
private static String longToPaddedString(long v, int digits_column_width) {
int digit_width = numDigits(v);
StringBuilder sb = new StringBuilder();
appendSpaces(sb, digits_column_width - digit_width);
sb.append(v);
return sb.toString();
}
/**
* Gets the number of digits in an integer when printed in base 10. Assumes
* a positive integer.
* @param n The value.
* @return The number of digits in the string.
*/
private static int numDigits(long n) {
int i = 0;
do {
i++;
n = n / 10;
} while (n > 0);
return i;
}
/**
* Gets a string of spaces of the length specified.
* @param sb The string builder to append to.
* @param numSpaces The number of spaces in the string.
*/
@VisibleForTesting
static void appendSpaces(StringBuilder sb, int numSpaces) {
if (numSpaces > 16) {
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> logger.warning("Tracer.appendSpaces called with large numSpaces");
// Avoid long loop in case some bug in the caller
numSpaces = 16;
}
while (numSpaces >= 5) {
sb.append(" ");
numSpaces -= 5;
}
// We know it's less than 5 now
switch (numSpaces) {
case 1:
sb.append(" ");
break;
case 2:
sb.append(" ");
break;
case 3:
sb.append(" ");
break;
case 4:
sb.append(" ");
break;
}
}
/**
* Adds a new tracing statistic to a trace
*
* @param tracingStatistic to enable a run
* @return The index of this statistic (for use with stat.extraInfo()), or
* -1 if the statistic is not enabled.
*/
static int addTracingStatistic(TracingStatistic tracingStatistic) {
// Check to see if we can enable the tracing statistic before actually
// adding it.
if (tracingStatistic.enable()) {
// No synchronization needed, since this is a copy-on-write array.
extraTracingStatistics.add(tracingStatistic);
// 99.9% of the time, this will be O(1) and return
// extraTracingStatistics.length - 1
return extraTracingStatistics.lastIndexOf(tracingStatistic);
} else {
return -1;
}
}
/**
* For testing purposes only. These removes all current tracers.
* Severe errors can occur if there are any active tracers going on
* when this is called.
*
* The test suite uses this to remove any tracers that it has added.
*/
@VisibleForTesting
static void clearTracingStatisticsTestingOnly() {
extraTracingStatistics.clear();
}
/**
* Stop the trace.
* This may only be done once and must be done from the same thread
* that started it.
* @param silence_threshold Traces for time less than silence_threshold
* ms will be left out of the trace report. A value of -1 indicates
* that the current ThreadTrace silence_threshold should be used.
* @return The time that this trace actually ran
*/
long stop(int
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>, getContextForNonEmptyExpression(context), true);
break;
default:
throw new Error("Unknown type " + type + "\n" + n.toStringTree());
}
cc.endSourceMapping(n);
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0 && s.charAt(0) != '0';
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
try {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
} catch (NumberFormatException e) {
// The number was too long to parse. Fall through to NaN.
}
}
return Double.NaN;
}
/**
* @return Whether the name is an indirect eval.
*/
private boolean isIndirectEval(Node n) {
return n.isName() && "eval".equals(n.getString()) &&
!n.getBooleanProp(Node.DIRECT_EVAL);
}
/**
* Adds a block or expression, substituting a VOID with an empty statement.
* This is used for "for (...);" and "if (...);" type statements.
*
* @param n The node to print.
* @param context The context to determine how the node should be printed.
*/
private void addNonEmptyStatement(
Node n, Context context, boolean allowNonBlockChild) {
Node nodeToProcess = n;
if (!allowNonBlockChild && !n.isBlock()) {
throw new Error("Missing BLOCK child.");
}
// Strip unneeded blocks, that is blocks with <2 children unless
// the CodePrinter specifically wants to keep them.
if (n.isBlock()) {
int count = getNonEmptyChildCount(n, 2);
if (count == 0) {
if (cc.shouldPreserveExtraBlocks()) {
cc.beginBlock();
cc.endBlock(cc.breakAfterBlockFor(n, context == Context.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>InList The first in the node list (chained through the next
* property).
*/
void addArrayList(Node firstInList) {
boolean lastWasEmpty = false;
for (Node n = firstInList; n != null; n = n.getNext()) {
if (n != firstInList) {
cc.listSeparator();
}
addExpr(n, 1);
lastWasEmpty = n.isEmpty();
}
if (lastWasEmpty) {
cc.listSeparator();
}
}
void addCaseBody(Node caseBody) {
cc.beginCaseBody();
add(caseBody);
cc.endCaseBody();
}
void addAllSiblings(Node n) {
for (Node c = n; c != null; c = c.getNext()) {
add(c);
}
}
/** Outputs a js string, using the optimal (single/double) quote character */
private void addJsString(Node n) {
String s = n.getString();
boolean useSlashV = n.getBooleanProp(Node.SLASH_V);
if (useSlashV) {
add(jsString(n.getString(), useSlashV));
} else {
String cached = ESCAPED_JS_STRINGS.get(s);
if (cached == null) {
cached = jsString(n.getString(), useSlashV);
ESCAPED_JS_STRINGS.put(s, cached);
}
add(cached);
}
}
private String jsString(String s, boolean useSlashV) {
int singleq = 0, doubleq = 0;
// could count the quotes and pick the optimal quote character
for (int i = 0; i < s.length(); i++) {
switch (s.charAt(i)) {
case '"': doubleq++; break;
case '\'': singleq++; break;
}
}
String doublequote, singlequote;
char quote;
if (singleq < doubleq) {
// more double quotes so escape the single quotes
quote = '\'';
doublequote = "\"";
singlequote = "\\\'";
} else {
// more single quotes so escape the doubles
quote = '\"';
doublequote = "\\\"";
singlequote = "\'";
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> }
return strEscape(s, quote, doublequote, singlequote, "\\\\",
outputCharsetEncoder, useSlashV);
}
/** Escapes regular expression */
static String regexpEscape(String s, CharsetEncoder outputCharsetEncoder) {
return strEscape(s, '/', "\"", "'", "\\", outputCharsetEncoder, false);
}
/**
* Escapes the given string to a double quoted (") JavaScript/JSON string
*/
static String escapeToDoubleQuotedJsString(String s) {
return strEscape(s, '"', "\\\"", "\'", "\\\\", null, false);
}
/* If the user doesn't want to specify an output charset encoder, assume
they want Latin/ASCII characters only.
*/
static String regexpEscape(String s) {
return regexpEscape(s, null);
}
/** Helper to escape javascript string as well as regular expression */
private static String strEscape(
String s, char quote,
String doublequoteEscape,
String singlequoteEscape,
String backslashEscape,
CharsetEncoder outputCharsetEncoder,
boolean useSlashV) {
StringBuilder sb = new StringBuilder(s.length() + 2);
sb.append(quote);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '\0': sb.append("\\x00"); break;
case '\u000B':
if (useSlashV) {
sb.append("\\v");
} else {
sb.append("\\x0B");
}
break;
case '\n': sb.append("\\n"); break;
case '\r': sb.append("\\r"); break;
case '\t': sb.append("\\t"); break;
case '\\': sb.append(backslashEscape); break;
case '\"': sb.append(doublequoteEscape); break;
case '\'': sb.append(singlequoteEscape); break;
case '>': // Break --> into --\> or ]]> into ]]\>
if (i >= 2 &&
((s.charAt(i - 1) == '-' && s.charAt(i - 2) == '-') ||
(s.charAt(i - 1) == ']' && s.charAt(i - 2
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>) == ']'))) {
sb.append("\\>");
} else {
sb.append(c);
}
break;
case '<':
// Break </script into <\/script
final String END_SCRIPT = "/script";
// Break <!-- into <\!--
final String START_COMMENT = "!--";
if (s.regionMatches(true, i + 1, END_SCRIPT, 0,
END_SCRIPT.length())) {
sb.append("<\\");
} else if (s.regionMatches(false, i + 1, START_COMMENT, 0,
START_COMMENT.length())) {
sb.append("<\\");
} else {
sb.append(c);
}
break;
default:
// If we're given an outputCharsetEncoder, then check if the
// character can be represented in this character set.
if (outputCharsetEncoder != null) {
if (outputCharsetEncoder.canEncode(c)) {
sb.append(c);
} else {
// Unicode-escape the character.
appendHexJavaScriptRepresentation(sb, c);
}
} else {
// No charsetEncoder provided - pass straight latin characters
// through, and escape the rest. Doing the explicit character
// check is measurably faster than using the CharsetEncoder.
if (c > 0x1f && c < 0x7f) {
sb.append(c);
} else {
// Other characters can be misinterpreted by some js parsers,
// or perhaps mangled by proxies along the way,
// so we play it safe and unicode escape them.
appendHexJavaScriptRepresentation(sb, c);
}
}
}
}
sb.append(quote);
return sb.toString();
}
static String identifierEscape(String s) {
// First check if escaping is needed at all -- in most cases it isn't.
if (NodeUtil.isLatin(s)) {
return s;
}
// Now going through the string to escape non-latin characters if needed.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// Identifiers should always go to Latin1/ ASCII characters because
// different browser
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> info.isNoShadow()) {
return true;
} else {
return false;
}
}
@Override public boolean equals(Object other) {
if (!(other instanceof Var)) {
return false;
}
Var otherVar = (Var) other;
return otherVar.nameNode == nameNode;
}
@Override public int hashCode() {
return nameNode.hashCode();
}
@Override
public String toString() {
return "Scope.Var " + name + "{" + type + "}";
}
/** Record that this is escaped by an inner scope. */
void markEscaped() {
markedEscaped = true;
}
/**
* Whether this is escaped by an inner scope.
* Notice that not all scope creators record this information.
*/
boolean isMarkedEscaped() {
return markedEscaped;
}
}
/**
* A special subclass of Var used to distinguish "arguments" in the current
* scope.
*/
// TODO(johnlenz): Include this the list of Vars for the scope.
public static class Arguments extends Var {
Arguments(Scope scope) {
super(
false, // no inferred
"arguments", // always arguments
null, // no declaration node
// TODO(johnlenz): provide the type of "Arguments".
null, // no type info
scope,
-1, // no variable index
null, // input,
false, // not a define
null // no jsdoc
);
}
@Override public boolean equals(Object other) {
if (!(other instanceof Arguments)) {
return false;
}
Arguments otherVar = (Arguments) other;
return otherVar.scope.getRootNode() == scope.getRootNode();
}
@Override public int hashCode() {
return System.identityHashCode(this);
}
}
/**
* Creates a Scope given the parent Scope and the root node of the scope.
* @param parent The parent Scope. Cannot be null.
* @param rootNode Typically the FUNCTION node.
*/
Scope(Scope parent, Node rootNode) {
Preconditions.checkNotNull(parent);
Preconditions.checkArgument(rootNode != parent.rootNode);
this.parent = parent;
this.rootNode = rootNode;
JSType nodeType = root
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Node.getJSType();
if (nodeType != null && nodeType.isFunctionType()) {
thisType = nodeType.toMaybeFunctionType().getTypeOfThis();
} else {
thisType = parent.thisType;
}
this.isBottom = false;
this.depth = parent.depth + 1;
}
/**
* Creates a global Scope.
* @param rootNode Typically the global BLOCK node.
*/
Scope(Node rootNode, AbstractCompiler compiler) {
this.parent = null;
this.rootNode = rootNode;
thisType = compiler.getTypeRegistry().getNativeObjectType(GLOBAL_THIS);
this.isBottom = false;
this.depth = 0;
}
/**
* Creates a empty Scope (bottom of the lattice).
* @param rootNode Typically a FUNCTION node or the global BLOCK node.
* @param thisType the type of {@code this} in this scope
*/
Scope(Node rootNode, ObjectType thisType) {
this.parent = null;
this.rootNode = rootNode;
this.thisType = thisType;
this.isBottom = true;
this.depth = 0;
}
/** The depth of the scope. The global scope has depth 0. */
int getDepth() {
return depth;
}
/** Whether this is the bottom of the lattice. */
boolean isBottom() {
return isBottom;
}
/**
* Gets the container node of the scope. This is typically the FUNCTION
* node or the global BLOCK/SCRIPT node.
*/
@Override
public Node getRootNode() {
return rootNode;
}
public Scope getParent() {
return parent;
}
Scope getGlobalScope() {
Scope result = this;
while (result.getParent() != null) {
result = result.getParent();
}
return result;
}
@Override
public StaticScope<JSType> getParentScope() {
return parent;
}
/**
* Gets the type of {@code this} in the current scope.
*/
@Override
public ObjectType getTypeOfThis() {
return thisType;
}
/**
* Declares a variable whose type is inferred.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.rhino.Node;
/**
* A simple pass to ensure that all AST nodes have line numbers,
* an that the line numbers are monotonically increasing.
*
* @author nicksantos@google.com (Nick Santos)
*/
class LineNumberCheck implements Callback, CompilerPass {
static final DiagnosticType MISSING_LINE_INFO = DiagnosticType.error(
"JSC_MISSING_LINE_INFO",
"No source location information associated with {0}.\n" +
"Most likely a Node has been created with settings the source file " +
"and line/column location. Usually this is done using " +
"Node.copyInformationFrom and supplying a Node from the source AST.");
private final AbstractCompiler compiler;
private boolean requiresLineNumbers = false;
LineNumberCheck(AbstractCompiler compiler) {
this.compiler = compiler;
}
public void setCheckSubTree(Node root) {
requiresLineNumbers = true;
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void process(Node externs, Node root) {
requiresLineNumbers = false;
NodeTraversal.traverse(compiler, root, this);
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Each JavaScript file is rooted in a script node, so we'll only
// have line number information inside the script node.
if (n.isScript()) {
requiresLineNumbers
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
Preconditions.checkNotNull(registerFunction);
return this.registerFunction == registerFunction;
}
boolean isGetterFunction() {
return registerFunction != null;
}
String getName() {
return name;
}
String getExpectedTypeName() {
return expectedTypeName;
}
Node createDefaultValueNode() {
switch (this) {
case REGISTER_BOOLEAN:
return IR.falseNode();
case REGISTER_NUMBER:
return IR.number(0);
case REGISTER_STRING:
return IR.string("");
}
throw new IllegalStateException();
}
}
// A map of function name -> TweakFunction.
private static final Map<String, TweakFunction> TWEAK_FUNCTIONS_MAP;
static {
TWEAK_FUNCTIONS_MAP = Maps.newHashMap();
for (TweakFunction func : TweakFunction.values()) {
TWEAK_FUNCTIONS_MAP.put(func.getName(), func);
}
}
ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks,
Map<String, Node> compilerDefaultValueOverrides) {
this.compiler = compiler;
this.stripTweaks = stripTweaks;
// Having the map sorted is required for the unit tests to be deterministic.
this.compilerDefaultValueOverrides = Maps.newTreeMap();
this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides);
}
@Override
public void process(Node externs, Node root) {
CollectTweaksResult result = collectTweaks(root);
applyCompilerDefaultValueOverrides(result.tweakInfos);
boolean changed = false;
if (stripTweaks) {
changed = stripAllCalls(result.tweakInfos);
} else if (!compilerDefaultValueOverrides.isEmpty()) {
changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls);
}
if (changed) {
compiler.reportCodeChange();
}
}
/**
* Passes the compiler default value overrides to the JS by replacing calls
* to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value;
*/
private boolean replaceGetCompilerOverridesCalls(
List<TweakFunctionCall> calls) {
for (TweakFunctionCall call : calls) {
Node callNode = call.callNode;
Node obj
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>DefaultValueOverrides(
Map<String, TweakInfo> tweakInfos) {
for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) {
String tweakId = entry.getKey();
TweakInfo tweakInfo = tweakInfos.get(tweakId);
if (tweakInfo == null) {
compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId));
} else {
TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc;
Node value = entry.getValue();
if (!registerFunc.isValidNodeType(value.getType())) {
compiler.report(JSError.make(INVALID_TWEAK_DEFAULT_VALUE_WARNING,
tweakId, registerFunc.getName(),
registerFunc.getExpectedTypeName()));
} else {
tweakInfo.defaultValueNode = value;
}
}
}
}
/**
* Finds all calls to goog.tweak functions and emits warnings/errors if any
* of the calls have issues.
* @return A map of {@link TweakInfo} structures, keyed by tweak ID.
*/
private CollectTweaksResult collectTweaks(Node root) {
CollectTweaks pass = new CollectTweaks();
NodeTraversal.traverse(compiler, root, pass);
Map<String, TweakInfo> tweakInfos = pass.allTweaks;
for (TweakInfo tweakInfo: tweakInfos.values()) {
tweakInfo.emitAllWarnings();
}
return new CollectTweaksResult(tweakInfos, pass.getOverridesCalls);
}
private final static class CollectTweaksResult {
final Map<String, TweakInfo> tweakInfos;
final List<TweakFunctionCall> getOverridesCalls;
CollectTweaksResult(Map<String, TweakInfo> tweakInfos,
List<TweakFunctionCall> getOverridesCalls) {
this.tweakInfos = tweakInfos;
this.getOverridesCalls = getOverridesCalls;
}
}
/**
* Processes all calls to goog.tweak functions.
*/
private final class CollectTweaks extends AbstractPostOrderCallback {
final Map<String, TweakInfo> allTweaks = Maps.newHashMap();
final List<TweakFunctionCall> getOverridesCalls = Lists.newArrayList();
@Override
public
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>() && !fnName.isEmpty()) {
typeRegistry.declareType(fnName, fnType.getInstanceType());
}
return fnType;
}
private void reportWarning(DiagnosticType warning, String ... args) {
compiler.report(JSError.make(sourceName, errorRoot, warning, args));
}
private void reportError(DiagnosticType error, String ... args) {
compiler.report(JSError.make(sourceName, errorRoot, error, args));
}
/**
* Determines whether the given jsdoc info declares a function type.
*/
static boolean isFunctionTypeDeclaration(JSDocInfo info) {
return info.getParameterCount() > 0 ||
info.hasReturnType() ||
info.hasThisType() ||
info.isConstructor() ||
info.isInterface();
}
/**
* The scope that we should declare this function in, if it needs
* to be declared in a scope. Notice that TypedScopeCreator takes
* care of most scope-declaring.
*/
private Scope getScopeDeclaredIn() {
int dotIndex = fnName.indexOf(".");
if (dotIndex != -1) {
String rootVarName = fnName.substring(0, dotIndex);
Var rootVar = scope.getVar(rootVarName);
if (rootVar != null) {
return rootVar.getScope();
}
}
return scope;
}
/**
* Check whether a type is resolvable in the future
* If this has a supertype that hasn't been resolved yet, then we can assume
* this type will be ok once the super type resolves.
* @param objectType
* @return true if objectType is resolvable in the future
*/
private static boolean hasMoreTagsToResolve(ObjectType objectType) {
Preconditions.checkArgument(objectType.isUnknownType());
if (objectType.getImplicitPrototype() != null) {
// constructor extends class
if (objectType.getImplicitPrototype().isResolved()) {
return false;
} else {
return true;
}
} else {
// interface extends interfaces
FunctionType ctor = objectType.getConstructor();
if (ctor != null) {
for (ObjectType interfaceType : ctor.getExtendedInterfaces()) {
if (!interfaceType.isResolved()) {
return
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> re-used for multiple check passes. Returns this for easy chaining.
*/
CheckGlobalNames injectNamespace(GlobalNamespace namespace) {
this.namespace = namespace;
return this;
}
@Override
public void process(Node externs, Node root) {
// TODO(nicksantos): Let CollapseProperties and CheckGlobalNames
// share a namespace.
if (namespace == null) {
namespace = new GlobalNamespace(compiler, root);
}
for (Name name : namespace.getNameForest()) {
checkDescendantNames(name, name.globalSets + name.localSets > 0);
}
}
/**
* Checks to make sure all the descendants of a name are defined if they
* are referenced.
*
* @param name A global name.
* @param nameIsDefined If true, {@code name} is defined. Otherwise, it's
* undefined, and any references to descendant names should emit warnings.
*/
private void checkDescendantNames(Name name, boolean nameIsDefined) {
if (name.props != null) {
for (Name prop : name.props) {
// if the ancestor of a property is not defined, then we should emit
// warnings for all references to the property.
boolean propIsDefined = false;
if (nameIsDefined) {
// if the ancestor of a property is defined, then let's check that
// the property is also explicitly defined if it needs to be.
propIsDefined = (!propertyMustBeInitializedByFullName(prop) ||
prop.globalSets + prop.localSets > 0);
}
validateName(prop, propIsDefined);
checkDescendantNames(prop, propIsDefined);
}
}
}
private void validateName(Name name, boolean isDefined) {
// If the name is not defined, emit warnings for each reference. While
// we're looking through each reference, check all the module dependencies.
Ref declaration = name.getDeclaration();
Name parent = name.parent;
boolean singleGlobalParentDecl =
parent != null &&
parent.getDeclaration() != null &&
parent.localSets == 0;
JSModuleGraph moduleGraph = compiler.getModuleGraph();
for (Ref ref : name.getRefs()) {
if (!isDefined && !isTypedef(ref)) {
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
reportRefToUndefinedName(name, ref);
} else if (declaration != null &&
ref.getModule() != declaration.getModule() &&
!moduleGraph.dependsOn(
ref.getModule(), declaration.getModule())) {
reportBadModuleReference(name, ref);
} else if (ref.scope.isGlobal() &&
singleGlobalParentDecl &&
parent.getDeclaration().preOrderIndex > ref.preOrderIndex) {
compiler.report(
JSError.make(ref.source.getName(), ref.node,
NAME_DEFINED_LATE_WARNING,
name.getFullName(),
parent.getFullName(),
parent.getDeclaration().source.getName(),
String.valueOf(parent.getDeclaration().node.getLineno())));
}
}
}
private boolean isTypedef(Ref ref) {
// If this is an annotated EXPR-GET, don't do anything.
Node parent = ref.node.getParent();
if (parent.isExprResult()) {
JSDocInfo info = ref.node.getJSDocInfo();
if (info != null && info.hasTypedefType()) {
return true;
}
}
return false;
}
private void reportBadModuleReference(Name name, Ref ref) {
compiler.report(
JSError.make(ref.source.getName(), ref.node, STRICT_MODULE_DEP_QNAME,
ref.getModule().getName(),
name.getDeclaration().getModule().getName(),
name.getFullName()));
}
private void reportRefToUndefinedName(Name name, Ref ref) {
// grab the highest undefined ancestor to output in the warning message.
while (name.parent != null &&
name.parent.globalSets + name.parent.localSets == 0) {
name = name.parent;
}
compiler.report(
JSError.make(ref.getSourceName(), ref.node, level,
UNDEFINED_NAME_WARNING, name.getFullName()));
}
/**
* Checks whether the given name is a property, and whether that property
* must be initialized with its full qualified name.
*/
private static boolean propertyMustBeInitializedByFullName(Name name) {
// If an object literal in the global namespace is never aliased,
// then all of its properties must be defined using its
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>classRelationship(type, subclass, superclass);
}
}
return null;
}
/**
* Determines whether the given node is a class-defining name, like
* "inherits" or "mixin."
* @return The type of class-defining name, or null.
*/
private SubclassType typeofClassDefiningName(Node callName) {
// Check if the method name matches one of the class-defining methods.
String methodName = null;
if (callName.isGetProp()) {
methodName = callName.getLastChild().getString();
} else if (callName.isName()) {
String name = callName.getString();
int dollarIndex = name.lastIndexOf('$');
if (dollarIndex != -1) {
methodName = name.substring(dollarIndex + 1);
}
}
if (methodName != null) {
if (methodName.equals("inherits")) {
return SubclassType.INHERITS;
} else if (methodName.equals("mixin")) {
return SubclassType.MIXIN;
}
}
return null;
}
@Override
public boolean isSuperClassReference(String propertyName) {
return "superClass_".equals(propertyName);
}
/**
* Given a qualified name node, returns whether "prototype" is at the end.
* For example:
* a.b.c => false
* a.b.c.prototype => true
*/
private boolean endsWithPrototype(Node qualifiedName) {
return qualifiedName.isGetProp() &&
qualifiedName.getLastChild().getString().equals("prototype");
}
/**
* Exctracts X from goog.provide('X'), if the applied Node is goog.
*
* @return The extracted class name, or null.
*/
@Override
public String extractClassNameIfProvide(Node node, Node parent){
return extractClassNameIfGoog(node, parent, "goog.provide");
}
/**
* Exctracts X from goog.require('X'), if the applied Node is goog.
*
* @return The extracted class name, or null.
*/
@Override
public String extractClassNameIfRequire(Node node, Node parent){
return extractClassNameIfGoog(node, parent, "goog.require");
}
private static String extract
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>(FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
functionType.defineDeclaredProperty("getInstance", getterType,
functionType.getSource());
functionType.defineDeclaredProperty("instance_", objectType,
functionType.getSource());
}
@Override
public String getGlobalObject() {
return "goog.global";
}
private final Set<String> propertyTestFunctions = ImmutableSet.of(
"goog.isDef", "goog.isNull", "goog.isDefAndNotNull",
"goog.isString", "goog.isNumber", "goog.isBoolean",
"goog.isFunction", "goog.isArray", "goog.isObject");
@Override
public boolean isPropertyTestFunction(Node call) {
Preconditions.checkArgument(call.isCall());
return propertyTestFunctions.contains(
call.getFirstChild().getQualifiedName());
}
@Override
public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
Node callNode) {
Preconditions.checkArgument(callNode.isCall());
Node callName = callNode.getFirstChild();
if (!"goog.reflect.object".equals(callName.getQualifiedName()) ||
callNode.getChildCount() != 3) {
return null;
}
Node typeNode = callName.getNext();
if (!typeNode.isQualifiedName()) {
return null;
}
Node objectNode = typeNode.getNext();
if (!objectNode.isObjectLit()) {
// TODO(johnlenz): The coding convention should not be performing checks.
t.getCompiler().report(JSError.make(t.getSourceName(), callNode,
OBJECTLIT_EXPECTED));
return null;
}
return new ObjectLiteralCast(typeNode.getQualifiedName(),
typeNode.getNext());
}
@Override
public boolean isOptionalParameter(Node parameter) {
return false;
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return false;
}
@Override
public boolean isPrivate(String name) {
return false;
}
@Override
public Collection<AssertionFunctionSpec> getAssertionFunctions() {
return ImmutableList.<AssertionFunctionSpec>of(
new AssertionFunctionSpec("goog.asserts.assert"),
new AssertionFunctionSpec("goog.asserts.assertNumber",
JSTypeNative.NUMBER_
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> SimpleSourceFile((String) value, false));
return;
}
removeProp(propType);
if (value != null) {
propListHead = createProp(propType, value, propListHead);
}
}
public void putBooleanProp(int propType, boolean value) {
putIntProp(propType, value ? 1 : 0);
}
public void putIntProp(int propType, int value) {
removeProp(propType);
if (value != 0) {
propListHead = createProp(propType, value, propListHead);
}
}
PropListItem createProp(int propType, Object value, PropListItem next) {
return new ObjectPropListItem(propType, value, next);
}
PropListItem createProp(int propType, int value, PropListItem next) {
return new IntPropListItem(propType, value, next);
}
// Gets all the property types, in sorted order.
private int[] getSortedPropTypes() {
int count = 0;
for (PropListItem x = propListHead; x != null; x = x.getNext()) {
count++;
}
int[] keys = new int[count];
for (PropListItem x = propListHead; x != null; x = x.getNext()) {
count--;
keys[count] = x.getType();
}
Arrays.sort(keys);
return keys;
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public double getDouble() throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with Node.newNumber");
} else {
throw new UnsupportedOperationException(this + " is not a number node");
}
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public void setDouble(double s) throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with Node.newNumber");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public String getString() throws UnsupportedOperationException {
if (
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public void setString(String s) throws UnsupportedOperationException {
if (this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
@Override
public String toString() {
return toString(true, true, true);
}
public String toString(
boolean printSource,
boolean printAnnotations,
boolean printType) {
StringBuilder sb = new StringBuilder();
toString(sb, printSource, printAnnotations, printType);
return sb.toString();
}
private void toString(
StringBuilder sb,
boolean printSource,
boolean printAnnotations,
boolean printType) {
sb.append(Token.name(type));
if (this instanceof StringNode) {
sb.append(' ');
sb.append(getString());
} else if (type == Token.FUNCTION) {
sb.append(' ');
// In the case of JsDoc trees, the first child is often not a string
// which causes exceptions to be thrown when calling toString or
// toStringTree.
if (first == null || first.getType() != Token.NAME) {
sb.append("<invalid>");
} else {
sb.append(first.getString());
}
} else if (type == Token.NUMBER) {
sb.append(' ');
sb.append(getDouble());
}
if (printSource) {
int lineno = getLineno();
if (lineno != -1) {
sb.append(' ');
sb.append(lineno);
}
}
if (printAnnotations) {
int[] keys = getSortedPropTypes();
for (int i = 0; i < keys.length; i++) {
int type = keys[i];
PropListItem x = lookupProperty(type);
sb.append(" [");
sb.append(propToString(type));
sb.append(": ");
String value;
switch (type) {
default:
value = x.toString
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>();
break;
}
sb.append(value);
sb.append(']');
}
}
if (printType) {
if (jsType != null) {
String jsTypeString = jsType.toString();
if (jsTypeString != null) {
sb.append(" : ");
sb.append(jsTypeString);
}
}
}
}
public String toStringTree() {
return toStringTreeImpl();
}
private String toStringTreeImpl() {
try {
StringBuilder s = new StringBuilder();
appendStringTree(s);
return s.toString();
} catch (IOException e) {
throw new RuntimeException("Should not happen\n" + e);
}
}
public void appendStringTree(Appendable appendable) throws IOException {
toStringTreeHelper(this, 0, appendable);
}
private static void toStringTreeHelper(Node n, int level, Appendable sb)
throws IOException {
for (int i = 0; i != level; ++i) {
sb.append(" ");
}
sb.append(n.toString());
sb.append('\n');
for (Node cursor = n.getFirstChild();
cursor != null;
cursor = cursor.getNext()) {
toStringTreeHelper(cursor, level + 1, sb);
}
}
int type; // type of the node; Token.NAME for example
Node next; // next sibling
private Node first; // first element of a linked list of children
private Node last; // last element of a linked list of children
/**
* Linked list of properties. Since vast majority of nodes would have
* no more then 2 properties, linked list saves memory and provides
* fast lookup. If this does not holds, propListHead can be replaced
* by UintMap.
*/
private PropListItem propListHead;
/**
* COLUMN_BITS represents how many of the lower-order bits of
* sourcePosition are reserved for storing the column number.
* Bits above these store the line number.
* This gives us decent position information for everything except
* files already passed through a minimizer, where lines might
* be longer than 4096 characters.
*/
public static final int COLUMN_BITS = 12;
/**
* MAX_COLUMN_NUMBER represents the
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> int post2 = node.getIntProp(INCRDECR_PROP);
if (post1 != post2) {
return false;
}
} else if (type == Token.STRING) {
int quoted1 = this.getIntProp(QUOTED_PROP);
int quoted2 = node.getIntProp(QUOTED_PROP);
if (quoted1 != quoted2) {
return false;
}
int slashV1 = this.getIntProp(SLASH_V);
int slashV2 = node.getIntProp(SLASH_V);
if (slashV1 != slashV2) {
return false;
}
} else if (type == Token.CALL) {
if (this.getBooleanProp(FREE_CALL) != node.getBooleanProp(FREE_CALL)) {
return false;
}
}
if (recurse) {
Node n, n2;
for (n = first, n2 = node.first;
n != null;
n = n.next, n2 = n2.next) {
if (!n.isEquivalentTo(n2, compareJsType, true)) {
return false;
}
}
}
return true;
}
/**
* This function takes a set of GETPROP nodes and produces a string that is
* each property separated by dots. If the node ultimately under the left
* sub-tree is not a simple name, this is not a valid qualified name.
*
* @return a null if this is not a qualified name, or a dot-separated string
* of the name and properties.
*/
public String getQualifiedName() {
if (type == Token.NAME) {
return getString();
} else if (type == Token.GETPROP) {
String left = getFirstChild().getQualifiedName();
if (left == null) {
return null;
}
return left + "." + getLastChild().getString();
} else if (type == Token.THIS) {
return "this";
} else {
return null;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name, such as
* <code>x</code> or <code>a.b.c</code> or <code>this.a</code>.
*/
public boolean isQualifiedName
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>() {
switch (getType()) {
case Token.NAME:
case Token.THIS:
return true;
case Token.GETPROP:
return getFirstChild().isQualifiedName();
default:
return false;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name without
* a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code>
* .
*/
public boolean isUnscopedQualifiedName() {
switch (getType()) {
case Token.NAME:
return true;
case Token.GETPROP:
return getFirstChild().isUnscopedQualifiedName();
default:
return false;
}
}
// ==========================================================================
// Mutators
/**
* Removes this node from its parent. Equivalent to:
* node.getParent().removeChild();
*/
public Node detachFromParent() {
Preconditions.checkState(parent != null);
parent.removeChild(this);
return this;
}
/**
* Removes the first child of Node. Equivalent to:
* node.removeChild(node.getFirstChild());
*
* @return The removed Node.
*/
public Node removeFirstChild() {
Node child = first;
if (child != null) {
removeChild(child);
}
return child;
}
/**
* @return A Node that is the head of the list of children.
*/
public Node removeChildren() {
Node children = first;
for (Node child = first; child != null; child = child.getNext()) {
child.parent = null;
}
first = null;
last = null;
return children;
}
/**
* Removes all children from this node and isolates the children from each
* other.
*/
public void detachChildren() {
for (Node child = first; child != null;) {
Node nextChild = child.getNext();
child.parent = null;
child.next = null;
child = nextChild;
}
first = null;
last = null;
}
public Node removeChildAfter(Node prev) {
Preconditions.checkArgument(prev.parent == this,
"prev is not a child of this node.");
Preconditions.checkArgument(prev.next != null,
"
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Maps;
import com.google.javascript.jscomp.CheckLevel;
import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSDocInfo.Visibility;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Map;
/**
* Insures '@constructor X' has a 'goog.provide("X")' .
*
*/
class CheckProvides implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
private final CheckLevel checkLevel;
private final CodingConvention codingConvention;
static final DiagnosticType MISSING_PROVIDE_WARNING = DiagnosticType.disabled(
"JSC_MISSING_PROVIDE",
"missing goog.provide(''{0}'')");
CheckProvides(AbstractCompiler compiler, CheckLevel checkLevel) {
this.compiler = compiler;
this.checkLevel = checkLevel;
this.codingConvention = compiler.getCodingConvention();
}
@Override
public void process(Node externs, Node root) {
hotSwapScript(root, null);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
CheckProvidesCallback callback =
new CheckProvidesCallback(codingConvention);
new NodeTraversal(compiler, callback).traverse(scriptRoot);
}
private class CheckProvidesCallback extends AbstractShallowCallback {
private final
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> Map<String, Node> provides = Maps.newHashMap();
private final Map<String, Node> ctors = Maps.newHashMap();
private final CodingConvention convention;
CheckProvidesCallback(CodingConvention convention){
this.convention = convention;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.CALL:
String providedClassName =
codingConvention.extractClassNameIfProvide(n, parent);
if (providedClassName != null) {
provides.put(providedClassName, n);
}
break;
case Token.FUNCTION:
visitFunctionNode(n, parent);
break;
case Token.SCRIPT:
visitScriptNode(t, n);
}
}
private void visitFunctionNode(Node n, Node parent) {
Node name = null;
JSDocInfo info = parent.getJSDocInfo();
if (info != null && info.isConstructor()) {
name = parent.getFirstChild();
} else {
// look to the child, maybe it's a named function
info = n.getJSDocInfo();
if (info != null && info.isConstructor()) {
name = n.getFirstChild();
}
}
if (name != null && name.isQualifiedName()) {
String qualifiedName = name.getQualifiedName();
if (!this.convention.isPrivate(qualifiedName)) {
Visibility visibility = info.getVisibility();
if (!visibility.equals(JSDocInfo.Visibility.PRIVATE)) {
ctors.put(qualifiedName, name);
}
}
}
}
private void visitScriptNode(NodeTraversal t, Node n) {
for (Map.Entry<String, Node> ctorEntry : ctors.entrySet()) {
if (!provides.containsKey(ctorEntry.getKey())) {
compiler.report(
t.makeError(ctorEntry.getValue(), checkLevel,
MISSING_PROVIDE_WARNING, ctorEntry.getKey()));
}
}
provides.clear();
ctors.clear();
}
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Native.NULL_TYPE));
register(getNativeType(JSTypeNative.NULL_TYPE), "Null");
register(getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE));
register(getNativeType(JSTypeNative.NUMBER_TYPE));
register(getNativeType(JSTypeNative.OBJECT_TYPE));
register(getNativeType(JSTypeNative.ERROR_TYPE));
register(getNativeType(JSTypeNative.URI_ERROR_TYPE));
register(getNativeType(JSTypeNative.EVAL_ERROR_TYPE));
register(getNativeType(JSTypeNative.TYPE_ERROR_TYPE));
register(getNativeType(JSTypeNative.RANGE_ERROR_TYPE));
register(getNativeType(JSTypeNative.REFERENCE_ERROR_TYPE));
register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE));
register(getNativeType(JSTypeNative.REGEXP_TYPE));
register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE));
register(getNativeType(JSTypeNative.STRING_TYPE));
register(getNativeType(JSTypeNative.VOID_TYPE));
register(getNativeType(JSTypeNative.VOID_TYPE), "Undefined");
register(getNativeType(JSTypeNative.VOID_TYPE), "void");
register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function");
}
private void register(JSType type) {
register(type, type.toString());
}
private void register(JSType type, String name) {
namesToTypes.put(name, type);
// Add all the namespaces in which this name lives.
while (name.indexOf('.') > 0) {
name = name.substring(0, name.lastIndexOf('.'));
namespaces.add(name);
}
}
private void registerNativeType(JSTypeNative typeId, JSType type) {
nativeTypes[typeId.ordinal()] = type;
}
/**
* Tells the type system that {@code owner} may have a property named
* {@code propertyName}. This allows the registry to keep track of what
* types a property is defined upon.
*
* This is NOT the same as saying that {@code owner} must have a property
* named type. ObjectType#hasProperty attempts
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> new FunctionBuilder(this)
.withParams(paramBuilder)
.withReturnType(returnType)
.withTypeOfThis(thisType)
.setIsConstructor(isConstructor)
.build();
}
throw new IllegalStateException(
"Unexpected node in type expression: " + n.toString());
}
/**
* Creates a RecordType from the nodes representing said record type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
private JSType createRecordTypeFromNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
RecordTypeBuilder builder = new RecordTypeBuilder(this);
// For each of the fields in the record type.
for (Node fieldTypeNode = n.getFirstChild();
fieldTypeNode != null;
fieldTypeNode = fieldTypeNode.getNext()) {
// Get the property's name.
Node fieldNameNode = fieldTypeNode;
boolean hasType = false;
if (fieldTypeNode.getType() == Token.COLON) {
fieldNameNode = fieldTypeNode.getFirstChild();
hasType = true;
}
String fieldName = fieldNameNode.getString();
// TODO(user): Move this into the lexer/parser.
// Remove the string literal characters around a field name,
// if any.
if (fieldName.startsWith("'") || fieldName.startsWith("\"")) {
fieldName = fieldName.substring(1, fieldName.length() - 1);
}
// Get the property's type.
JSType fieldType = null;
if (hasType) {
// We have a declared type.
fieldType = createFromTypeNodesInternal(
fieldTypeNode.getLastChild(), sourceName, scope);
} else {
// Otherwise, the type is UNKNOWN.
fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
// Add the property to the record.
if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) {
// Duplicate field name, warning and skip
reporter.warning(
"Duplicate record field " + fieldName,
sourceName,
n.getLineno(), fieldNameNode.getCharno());
}
}
return builder.build();
}
/**
* Sets the template type name.
*/
public void setTemplateTypeName
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.resolve(typeParsingErrorReporter, scope));
}
}
TypedScopeCreator(AbstractCompiler compiler) {
this(compiler, compiler.getCodingConvention());
}
TypedScopeCreator(AbstractCompiler compiler,
CodingConvention codingConvention) {
this.compiler = compiler;
this.validator = compiler.getTypeValidator();
this.codingConvention = codingConvention;
this.typeRegistry = compiler.getTypeRegistry();
this.typeParsingErrorReporter = typeRegistry.getErrorReporter();
}
/**
* Creates a scope with all types declared. Declares newly discovered types
* and type properties in the type registry.
*/
@Override
public Scope createScope(Node root, Scope parent) {
// Constructing the global scope is very different than constructing
// inner scopes, because only global scopes can contain named classes that
// show up in the type registry.
Scope newScope = null;
AbstractScopeBuilder scopeBuilder = null;
if (parent == null) {
// Run a first-order analysis over the syntax tree.
(new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults))
.process(root.getFirstChild(), root.getLastChild());
// Find all the classes in the global scope.
newScope = createInitialScope(root);
GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope);
scopeBuilder = globalScopeBuilder;
NodeTraversal.traverse(compiler, root, scopeBuilder);
} else {
newScope = new Scope(parent, root);
LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope);
scopeBuilder = localScopeBuilder;
localScopeBuilder.build();
}
scopeBuilder.resolveStubDeclarations();
scopeBuilder.resolveTypes();
// Gather the properties in each function that we found in the
// global scope, if that function has a @this type that we can
// build properties on.
for (Node functionNode : scopeBuilder.nonExternFunctions) {
JSType type = functionNode.getJSType();
if (type != null && type.isFunctionType()) {
FunctionType fnType = type.toMaybeFunctionType();
ObjectType fnThisType = fnType.getTypeOfThis();
if (!fnThisType.isUnknownType()) {
NodeTraversal.traverse(compiler, functionNode.getLastChild(),
scopeBuilder.new CollectProperties
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> scope. This scope contains native binding such as
* {@code Object}, {@code Date}, etc.
*/
@VisibleForTesting
Scope createInitialScope(Node root) {
NodeTraversal.traverse(
compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry));
Scope s = new Scope(root, compiler);
declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE);
declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, DATE_FUNCTION_TYPE);
declareNativeFunctionType(s, ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE);
declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE);
declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE);
declareNativeValueType(s, "undefined", VOID_TYPE);
// ActiveXObject is unqiuely special, because it can be used to construct
// any type (the type that it creates is related to the arguments you
// pass to it).
declareNativeValueType(s, "ActiveXObject", NO_OBJECT_TYPE);
return s;
}
private void declareNativeFunctionType(Scope scope, JSTypeNative tId) {
FunctionType t = typeRegistry.getNativeFunctionType(tId);
declareNativeType(scope, t.getInstanceType().getReferenceName(), t);
declareNativeType(
scope, t.getPrototype().getReferenceName(), t.getPrototype());
}
private void declareNativeValueType(Scope scope, String name,
JSTypeNative tId) {
declareNativeType(scope, name, typeRegistry.getNativeType(tId));
}
private void declareNativeType(Scope scope, String name, JSType t) {
scope.declare(name, null, t
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>, null, false);
}
private static class DiscoverEnumsAndTypedefs
extends AbstractShallowStatementCallback {
private final JSTypeRegistry registry;
DiscoverEnumsAndTypedefs(JSTypeRegistry registry) {
this.registry = registry;
}
@Override
public void visit(NodeTraversal t, Node node, Node parent) {
Node nameNode = null;
switch (node.getType()) {
case Token.VAR:
for (Node child = node.getFirstChild();
child != null; child = child.getNext()) {
identifyNameNode(
child, child.getFirstChild(),
NodeUtil.getBestJSDocInfo(child));
}
break;
case Token.EXPR_RESULT:
Node firstChild = node.getFirstChild();
if (firstChild.isAssign()) {
identifyNameNode(
firstChild.getFirstChild(), firstChild.getLastChild(),
firstChild.getJSDocInfo());
} else {
identifyNameNode(
firstChild, null, firstChild.getJSDocInfo());
}
break;
}
}
private void identifyNameNode(
Node nameNode, Node valueNode, JSDocInfo info) {
if (nameNode.isQualifiedName()) {
if (info != null) {
if (info.hasEnumParameterType()) {
registry.identifyNonNullableName(nameNode.getQualifiedName());
} else if (info.hasTypedefType()) {
registry.identifyNonNullableName(nameNode.getQualifiedName());
}
}
}
}
}
private JSType getNativeType(JSTypeNative nativeType) {
return typeRegistry.getNativeType(nativeType);
}
private abstract class AbstractScopeBuilder
implements NodeTraversal.Callback {
/**
* The scope that we're builidng.
*/
final Scope scope;
private final List<DeferredSetType> deferredSetTypes =
Lists.newArrayList();
/**
* Functions that we found in the global scope and not in externs.
*/
private final List<Node> nonExternFunctions = Lists.newArrayList();
/**
* Type-less stubs.
*
* If at the end of traversal, we still don't have types for these
* stubs, then we should declare UNKNOWN types.
*/
private final List<StubDeclaration>
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> stubDeclarations =
Lists.newArrayList();
/**
* The current source file that we're in.
*/
private String sourceName = null;
/**
* The InputId of the current node.
*/
private InputId inputId;
private AbstractScopeBuilder(Scope scope) {
this.scope = scope;
}
void setDeferredType(Node node, JSType type) {
deferredSetTypes.add(new DeferredSetType(node, type));
}
void resolveTypes() {
// Resolve types and attach them to nodes.
for (DeferredSetType deferred : deferredSetTypes) {
deferred.resolve(scope);
}
// Resolve types and attach them to scope slots.
Iterator<Var> vars = scope.getVars();
while (vars.hasNext()) {
vars.next().resolveType(typeParsingErrorReporter);
}
// Tell the type registry that any remaining types
// are unknown.
typeRegistry.resolveTypesInScope(scope);
}
@Override
public final boolean shouldTraverse(NodeTraversal t, Node n,
Node parent) {
inputId = t.getInputId();
if (n.isFunction() ||
n.isScript()) {
Preconditions.checkNotNull(inputId);
sourceName = NodeUtil.getSourceName(n);
}
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
boolean descend = parent == null || !parent.isFunction() ||
n == parent.getFirstChild() || parent == scope.getRootNode();
if (descend) {
// Handle hoisted functions on pre-order traversal, so that they
// get hit before other things in the scope.
if (NodeUtil.isStatementParent(n)) {
for (Node child = n.getFirstChild();
child != null;
child = child.getNext()) {
if (NodeUtil.isHoistedFunctionDeclaration(child)) {
defineFunctionLiteral(child, n);
}
}
}
}
return descend;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
inputId = t.getInputId();
attachLiteralTypes(t, n);
switch (n.getType()) {
case Token.CALL:
checkForClassDefining
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Calls(t, n, parent);
checkForCallingConventionDefiningCalls(n, delegateCallingConventions);
break;
case Token.FUNCTION:
if (t.getInput() == null || !t.getInput().isExtern()) {
nonExternFunctions.add(n);
}
// Hoisted functions are handled during pre-traversal.
if (!NodeUtil.isHoistedFunctionDeclaration(n)) {
defineFunctionLiteral(n, parent);
}
break;
case Token.ASSIGN:
// Handle initialization of properties.
Node firstChild = n.getFirstChild();
if (firstChild.isGetProp() &&
firstChild.isQualifiedName()) {
maybeDeclareQualifiedName(t, n.getJSDocInfo(),
firstChild, n, firstChild.getNext());
}
break;
case Token.CATCH:
defineCatch(n, parent);
break;
case Token.VAR:
defineVar(n, parent);
break;
case Token.GETPROP:
// Handle stubbed properties.
if (parent.isExprResult() &&
n.isQualifiedName()) {
maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null);
}
break;
}
}
private void attachLiteralTypes(NodeTraversal t, Node n) {
switch (n.getType()) {
case Token.NULL:
n.setJSType(getNativeType(NULL_TYPE));
break;
case Token.VOID:
n.setJSType(getNativeType(VOID_TYPE));
break;
case Token.STRING:
// Defer keys to the Token.OBJECTLIT case
if (!NodeUtil.isObjectLitKey(n, n.getParent())) {
n.setJSType(getNativeType(STRING_TYPE));
}
break;
case Token.NUMBER:
n.setJSType(getNativeType(NUMBER_TYPE));
break;
case Token.TRUE:
case Token.FALSE:
n.setJSType(getNativeType(BOOLEAN_TYPE));
break;
case Token.REGEXP:
n.setJSType(getNativeType(REGEXP_TYPE));
break;
case Token.OBJECTLIT:
defineObjectLiteral(t, n);
break;
// NOTE(nicksantos): If we
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> ever support Array tuples,
// we will need to put ARRAYLIT here as well.
}
}
private void defineObjectLiteral(NodeTraversal t, Node objectLit) {
// Handle the @lends annotation.
JSType type = null;
JSDocInfo info = objectLit.getJSDocInfo();
if (info != null &&
info.getLendsName() != null) {
String lendsName = info.getLendsName();
Var lendsVar = scope.getVar(lendsName);
if (lendsVar == null) {
compiler.report(
JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName));
} else {
type = lendsVar.getType();
if (type == null) {
type = typeRegistry.getNativeType(UNKNOWN_TYPE);
}
if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) {
compiler.report(
JSError.make(sourceName, objectLit, LENDS_ON_NON_OBJECT,
lendsName, type.toString()));
type = null;
} else {
objectLit.setJSType(type);
}
}
}
info = NodeUtil.getBestJSDocInfo(objectLit);
Node lValue = NodeUtil.getBestLValue(objectLit);
String lValueName = NodeUtil.getBestLValueName(lValue);
boolean createdEnumType = false;
if (info != null && info.hasEnumParameterType()) {
type = createEnumTypeFromNodes(objectLit, lValueName, info, lValue);
createdEnumType = true;
}
if (type == null) {
type = typeRegistry.createAnonymousObjectType();
}
setDeferredType(objectLit, type);
// If this is an enum, the properties were already taken care of above.
processObjectLitProperties(
t, objectLit, ObjectType.cast(objectLit.getJSType()), !createdEnumType);
}
/**
* Process an object literal and all the types on it.
* @param objLit The OBJECTLIT node.
* @param objLitType The type of the OBJECTLIT node. This might be a named
* type, because of the lends annotation.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> * @param declareOnOwner If true, declare properties on the objLitType as
* well. If false, the caller should take crae of this.
*/
void processObjectLitProperties(
NodeTraversal t, Node objLit, ObjectType objLitType,
boolean declareOnOwner) {
for (Node keyNode = objLit.getFirstChild(); keyNode != null;
keyNode = keyNode.getNext()) {
Node value = keyNode.getFirstChild();
String memberName = NodeUtil.getObjectLitKeyName(keyNode);
JSDocInfo info = keyNode.getJSDocInfo();
JSType valueType =
getDeclaredType(t.getSourceName(), info, keyNode, value);
JSType keyType = objLitType.isEnumType() ?
objLitType.toMaybeEnumType().getElementsType() :
NodeUtil.getObjectLitKeyTypeFromValueType(keyNode, valueType);
// Try to declare this property in the current scope if it
// has an authoritative name.
String qualifiedName = NodeUtil.getBestLValueName(keyNode);
if (qualifiedName != null) {
boolean inferred = keyType == null;
defineSlot(keyNode, objLit, qualifiedName, keyType, inferred);
} else if (keyType != null) {
setDeferredType(keyNode, keyType);
}
if (keyType != null && objLitType != null && declareOnOwner) {
// Declare this property on its object literal.
boolean isExtern = t.getInput() != null && t.getInput().isExtern();
objLitType.defineDeclaredProperty(memberName, keyType, keyNode);
}
}
}
/**
* Returns the type specified in a JSDoc annotation near a GETPROP or NAME.
*
* Extracts type information from either the {@code @type} tag or from
* the {@code @return} and {@code @param} tags.
*/
private JSType getDeclaredTypeInAnnotation(String sourceName,
Node node, JSDocInfo info) {
JSType jsType = null;
Node objNode =
node.isGetProp() ? node.getFirstChild() :
NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() :
null;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> if (info != null) {
if (info.hasType()) {
jsType = info.getType().evaluate(scope, typeRegistry);
} else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) {
String fnName = node.getQualifiedName();
jsType = createFunctionTypeFromNodes(
null, fnName, info, node);
}
}
return jsType;
}
/**
* Asserts that it's ok to define this node's name.
* The node should have a source name and be of the specified type.
*/
void assertDefinitionNode(Node n, int type) {
Preconditions.checkState(sourceName != null);
Preconditions.checkState(n.getType() == type);
}
/**
* Defines a catch parameter.
*/
void defineCatch(Node n, Node parent) {
assertDefinitionNode(n, Token.CATCH);
Node catchName = n.getFirstChild();
defineSlot(catchName, n, null);
}
/**
* Defines a VAR initialization.
*/
void defineVar(Node n, Node parent) {
assertDefinitionNode(n, Token.VAR);
JSDocInfo info = n.getJSDocInfo();
if (n.hasMoreThanOneChild()) {
if (info != null) {
// multiple children
compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF));
}
for (Node name : n.children()) {
defineName(name, n, parent, name.getJSDocInfo());
}
} else {
Node name = n.getFirstChild();
defineName(name, n, parent,
(info != null) ? info : name.getJSDocInfo());
}
}
/**
* Defines a function literal.
*/
void defineFunctionLiteral(Node n, Node parent) {
assertDefinitionNode(n, Token.FUNCTION);
// Determine the name and JSDocInfo and lvalue for the function.
// Any of these may be null.
Node lValue = NodeUtil.getBestLValue(n);
JSDocInfo info = NodeUtil.getBestJSDocInfo(n);
String functionName = NodeUtil.getBestLValueName(lValue);
FunctionType functionType =
createFunctionType
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>FromNodes(n, functionName, info, lValue);
// Assigning the function type to the function node
setDeferredType(n, functionType);
// Declare this symbol in the current scope iff it's a function
// declaration. Otherwise, the declaration will happen in other
// code paths.
if (NodeUtil.isFunctionDeclaration(n)) {
defineSlot(n.getFirstChild(), n, functionType);
}
}
/**
* Defines a variable based on the {@link Token#NAME} node passed.
* @param name The {@link Token#NAME} node.
* @param var The parent of the {@code name} node, which must be a
* {@link Token#VAR} node.
* @param parent {@code var}'s parent.
* @param info the {@link JSDocInfo} information relating to this
* {@code name} node.
*/
private void defineName(Node name, Node var, Node parent, JSDocInfo info) {
Node value = name.getFirstChild();
// variable's type
JSType type = getDeclaredType(sourceName, info, name, value);
if (type == null) {
// The variable's type will be inferred.
type = name.isFromExterns() ?
getNativeType(UNKNOWN_TYPE) : null;
}
defineSlot(name, var, type);
}
/**
* If a variable is assigned a function literal in the global scope,
* make that a declared type (even if there's no doc info).
* There's only one exception to this rule:
* if the return type is inferred, and we're in a local
* scope, we should assume the whole function is inferred.
*/
private boolean shouldUseFunctionLiteralType(
FunctionType type, JSDocInfo info, Node lValue) {
if (info != null) {
return true;
}
if (lValue != null &&
NodeUtil.isObjectLitKey(lValue, lValue.getParent())) {
return false;
}
return scope.isGlobal() || !type.isReturnTypeInferred();
}
/**
* Creates a new function type, based on the given nodes.
*
* This handles two cases that are semantically very different, but
* are not mutually
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> exclusive:
* - A function literal that needs a type attached to it.
* - An assignment expression with function-type info in the jsdoc.
*
* All parameters are optional, and we will do the best we can to create
* a function type.
*
* This function will always create a function type, so only call it if
* you're sure that's what you want.
*
* @param rValue The function node.
* @param name the function's name
* @param info the {@link JSDocInfo} attached to the function definition
* @param lvalueNode The node where this function is being
* assigned. For example, {@code A.prototype.foo = ...} would be used to
* determine that this function is a method of A.prototype. May be
* null to indicate that this is not being assigned to a qualified name.
*/
private FunctionType createFunctionTypeFromNodes(
@Nullable Node rValue,
@Nullable String name,
@Nullable JSDocInfo info,
@Nullable Node lvalueNode) {
FunctionType functionType = null;
// Global ctor aliases should be registered with the type registry.
if (rValue != null && rValue.isQualifiedName() && scope.isGlobal()) {
Var var = scope.getVar(rValue.getQualifiedName());
if (var != null && var.getType() != null &&
var.getType().isFunctionType()) {
FunctionType aliasedType = var.getType().toMaybeFunctionType();
if ((aliasedType.isConstructor() || aliasedType.isInterface()) &&
!aliasedType.isNativeObjectType()) {
functionType = aliasedType;
if (name != null && scope.isGlobal()) {
typeRegistry.declareType(name, functionType.getInstanceType());
}
}
}
}
if (functionType == null) {
Node errorRoot = rValue == null ? lvalueNode : rValue;
boolean isFnLiteral =
rValue != null && rValue.isFunction();
Node fnRoot = isFnLiteral ? rValue : null;
Node parametersNode = isFnLiteral ?
rValue.getFirstChild().getNext() : null;
Node fnBlock = isFnLiteral ? parametersNode.getNext() : null;
if (info != null && info
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.hasType()) {
JSType type = info.getType().evaluate(scope, typeRegistry);
// Known to be not null since we have the FUNCTION token there.
type = type.restrictByNotNullOrUndefined();
if (type.isFunctionType()) {
functionType = type.toMaybeFunctionType();
functionType.setJSDocInfo(info);
}
}
if (functionType == null) {
// Find the type of any overridden function.
Node ownerNode = NodeUtil.getBestLValueOwner(lvalueNode);
String ownerName = NodeUtil.getBestLValueName(ownerNode);
Var ownerVar = null;
String propName = null;
ObjectType ownerType = null;
if (ownerName != null) {
ownerVar = scope.getVar(ownerName);
if (ownerVar != null) {
ownerType = ObjectType.cast(ownerVar.getType());
}
if (name != null) {
propName = name.substring(ownerName.length() + 1);
}
}
FunctionType overriddenPropType = null;
if (ownerType != null && propName != null) {
overriddenPropType =
findOverriddenFunction(ownerType, propName);
}
FunctionTypeBuilder builder =
new FunctionTypeBuilder(name, compiler, errorRoot, sourceName,
scope)
.setContents(getFunctionAnalysisResults(fnRoot))
.inferFromOverriddenFunction(overriddenPropType, parametersNode)
.inferTemplateTypeName(info)
.inferReturnType(info)
.inferInheritance(info);
// Infer the context type.
boolean searchedForThisType = false;
if (ownerType != null && ownerType.isFunctionPrototypeType()) {
builder.inferThisType(
info, ownerType.getOwnerFunction().getInstanceType());
searchedForThisType = true;
} else if (ownerNode != null && ownerNode.isThis()) {
builder.inferThisType(info, ownerNode.getJSType());
searchedForThisType = true;
}
if (!searchedForThisType) {
builder.inferThisType(info);
}
functionType = builder
.inferParameterTypes(parametersNode, info)
.buildAndRegister();
}
}
// all done
return functionType;
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
/**
* Find the function that's being overridden on this type, if any.
*/
private FunctionType findOverriddenFunction(
ObjectType ownerType, String propName) {
// First, check to see if the property is implemented
// on a superclass.
JSType propType = ownerType.getPropertyType(propName);
if (propType != null && propType.isFunctionType()) {
return propType.toMaybeFunctionType();
} else {
// If it's not, then check to see if it's implemented
// on an implemented interface.
for (ObjectType iface :
ownerType.getCtorImplementedInterfaces()) {
propType = iface.getPropertyType(propName);
if (propType != null && propType.isFunctionType()) {
return propType.toMaybeFunctionType();
}
}
}
return null;
}
/**
* Creates a new enum type, based on the given nodes.
*
* This handles two cases that are semantically very different, but
* are not mutually exclusive:
* - An object literal that needs an enum type attached to it.
* - An assignment expression with an enum tag in the jsdoc.
*
* This function will always create an enum type, so only call it if
* you're sure that's what you want.
*
* @param rValue The node of the enum.
* @param name The enum's name
* @param info The {@link JSDocInfo} attached to the enum definition.
* @param lValueNode The node where this function is being
* assigned.
*/
private EnumType createEnumTypeFromNodes(Node rValue, String name,
JSDocInfo info, Node lValueNode) {
Preconditions.checkNotNull(info);
Preconditions.checkState(info.hasEnumParameterType());
EnumType enumType = null;
if (rValue != null && rValue.isQualifiedName()) {
// Handle an aliased enum.
Var var = scope.getVar(rValue.getQualifiedName());
if (var != null && var.getType() instanceof EnumType) {
enumType = (EnumType) var.getType();
}
}
if (enumType == null) {
JSType elementsType =
info.getEnumParameterType().evaluate(scope, typeRegistry);
enumType = typeRegistry
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>.createEnumType(name, rValue, elementsType);
if (rValue != null && rValue.isObjectLit()) {
// collect enum elements
Node key = rValue.getFirstChild();
while (key != null) {
String keyName = NodeUtil.getStringValue(key);
if (keyName == null) {
// GET and SET don't have a String value;
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else if (!codingConvention.isValidEnumKey(keyName)) {
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else {
enumType.defineElement(keyName, key);
}
key = key.getNext();
}
}
}
if (name != null && scope.isGlobal()) {
typeRegistry.declareType(name, enumType.getElementsType());
}
return enumType;
}
/**
* Defines a typed variable. The defining node will be annotated with the
* variable's type or {@code null} if its type is inferred.
* @param name the defining node. It must be a {@link Token#NAME}.
* @param parent the {@code name}'s parent.
* @param type the variable's type. It may be {@code null}, in which case
* the variable's type will be inferred.
*/
private void defineSlot(Node name, Node parent, JSType type) {
defineSlot(name, parent, type, type == null);
}
/**
* Defines a typed variable. The defining node will be annotated with the
* variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is
* inferred.
*
* Slots may be any variable or any qualified name in the global scope.
*
* @param n the defining NAME or GETPROP node.
* @param parent the {@code n}'s parent.
* @param type the variable's type. It may be {@code null} if
* {@code inferred} is {@code true}.
*/
void defineSlot(Node n, Node parent, JSType type, boolean inferred) {
Preconditions.checkArgument(inferred || type != null);
// Only allow declarations
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>inputId);
if (scopeToDeclareIn.isDeclared(variableName, false)) {
Var oldVar = scopeToDeclareIn.getVar(variableName);
newVar = validator.expectUndeclaredVariable(
sourceName, input, n, parent, oldVar, variableName, type);
} else {
if (!inferred) {
setDeferredType(n, type);
}
newVar =
scopeToDeclareIn.declare(variableName, n, type, input, inferred);
if (type instanceof EnumType) {
Node initialValue = newVar.getInitialValue();
boolean isValidValue = initialValue != null &&
(initialValue.isObjectLit() ||
initialValue.isQualifiedName());
if (!isValidValue) {
compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER));
}
}
}
// We need to do some additional work for constructors and interfaces.
FunctionType fnType = JSType.toMaybeFunctionType(type);
if (fnType != null &&
// We don't want to look at empty function types.
!type.isEmptyType()) {
if ((fnType.isConstructor() || fnType.isInterface()) &&
!fnType.equals(getNativeType(U2U_CONSTRUCTOR_TYPE))) {
// Declare var.prototype in the scope chain.
FunctionType superClassCtor = fnType.getSuperClassConstructor();
ObjectType.Property prototypeSlot = fnType.getSlot("prototype");
// When we declare the function prototype implicitly, we
// want to make sure that the function and its prototype
// are declared at the same node. We also want to make sure
// that the if a symbol has both a Var and a JSType, they have
// the same node.
//
// This consistency is helpful to users of SymbolTable,
// because everything gets declared at the same place.
prototypeSlot.setNode(n);
String prototypeName = variableName + ".prototype";
// There are some rare cases where the prototype will already
// be declared. See TypedScopeCreatorTest#testBogusPrototypeInit.
// Fortunately, other warnings will complain if this happens.
Var prototypeVar = scopeToDeclareIn.getVar(prototypeName);
if (prototypeVar != null && prototypeVar.scope == scopeToDeclare
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> the scope for the name of the given node.
*/
private Scope getQnameRootScope(Node n) {
Node root = NodeUtil.getRootOfQualifiedName(n);
if (root.isName()) {
Var var = scope.getVar(root.getString());
if (var != null) {
return var.getScope();
}
}
return null;
}
/**
* Look for a type declaration on a property assignment
* (in an ASSIGN or an object literal key).
*
* @param info The doc info for this property.
* @param lValue The l-value node.
* @param rValue The node that {@code n} is being initialized to,
* or {@code null} if this is a stub declaration.
*/
private JSType getDeclaredType(String sourceName, JSDocInfo info,
Node lValue, @Nullable Node rValue) {
if (info != null && info.hasType()) {
return getDeclaredTypeInAnnotation(sourceName, lValue, info);
} else if (rValue != null && rValue.isFunction() &&
shouldUseFunctionLiteralType(
JSType.toMaybeFunctionType(rValue.getJSType()), info, lValue)) {
return rValue.getJSType();
} else if (info != null) {
if (info.hasEnumParameterType()) {
if (rValue != null && rValue.isObjectLit()) {
return rValue.getJSType();
} else {
return createEnumTypeFromNodes(
rValue, lValue.getQualifiedName(), info, lValue);
}
} else if (info.isConstructor() || info.isInterface()) {
return createFunctionTypeFromNodes(
rValue, lValue.getQualifiedName(), info, lValue);
} else {
// Check if this is constant, and if it has a known type.
if (info.isConstant()) {
JSType knownType = null;
if (rValue != null) {
if (rValue.getJSType() != null
&& !rValue.getJSType().isUnknownType()) {
return rValue.getJSType();
} else if (rValue.isOr()) {
// Check for a very specific JS idiom:
// var x = x || TYPE;
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
// This is used by Closure's base namespace for esoteric
// reasons.
Node firstClause = rValue.getFirstChild();
Node secondClause = firstClause.getNext();
boolean namesMatch = firstClause.isName()
&& lValue.isName()
&& firstClause.getString().equals(lValue.getString());
if (namesMatch && secondClause.getJSType() != null
&& !secondClause.getJSType().isUnknownType()) {
return secondClause.getJSType();
}
}
}
}
}
}
return getDeclaredTypeInAnnotation(sourceName, lValue, info);
}
private FunctionType getFunctionType(@Nullable Var v) {
JSType t = v == null ? null : v.getType();
ObjectType o = t == null ? null : t.dereference();
return JSType.toMaybeFunctionType(o);
}
/**
* Look for calls that set a delegate method's calling convention.
*/
private void checkForCallingConventionDefiningCalls(
Node n, Map<String, String> delegateCallingConventions) {
codingConvention.checkForCallingConventionDefiningCalls(n,
delegateCallingConventions);
}
/**
* Look for class-defining calls.
* Because JS has no 'native' syntax for defining classes,
* this is often very coding-convention dependent and business-logic heavy.
*/
private void checkForClassDefiningCalls(
NodeTraversal t, Node n, Node parent) {
SubclassRelationship relationship =
codingConvention.getClassesDefinedByCall(n);
if (relationship != null) {
FunctionType superCtor = getFunctionType(
scope.getVar(relationship.superclassName));
FunctionType subCtor = getFunctionType(
scope.getVar(relationship.subclassName));
if (superCtor != null && superCtor.isConstructor() &&
subCtor != null && subCtor.isConstructor()) {
ObjectType superClass = superCtor.getInstanceType();
ObjectType subClass = subCtor.getInstanceType();
// superCtor and subCtor might be structural constructors
// (like {function(new:Object)}) so we need to resolve them back
// to the original ctor objects.
superCtor = superClass.getConstructor();
subCtor = subClass.getConstructor();
if (relationship.type == SubclassType.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> a terrible, terrible hack
// to bail out on recusive typedefs. We'll eventually need
// to handle these properly.
typeRegistry.declareType(typedef, getNativeType(UNKNOWN_TYPE));
JSType realType = info.getTypedefType().evaluate(scope, typeRegistry);
if (realType == null) {
compiler.report(
JSError.make(
t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef));
}
typeRegistry.overwriteDeclaredType(typedef, realType);
if (candidate.isGetProp()) {
defineSlot(candidate, candidate.getParent(),
getNativeType(NO_TYPE), false);
}
}
} // end GlobalScopeBuilder
/**
* A shallow traversal of a local scope to find all arguments and
* local variables.
*/
private final class LocalScopeBuilder extends AbstractScopeBuilder {
/**
* @param scope The scope that we're builidng.
*/
private LocalScopeBuilder(Scope scope) {
super(scope);
}
/**
* Traverse the scope root and build it.
*/
void build() {
NodeTraversal.traverse(compiler, scope.getRootNode(), this);
AstFunctionContents contents =
getFunctionAnalysisResults(scope.getRootNode());
if (contents != null) {
for (String varName : contents.getEscapedVarNames()) {
Var v = scope.getVar(varName);
Preconditions.checkState(v.getScope() == scope);
v.markEscaped();
}
}
}
/**
* Visit a node in a local scope, and add any local variables or catch
* parameters into the local symbol table.
*
* @param t The node traversal.
* @param n The node being visited.
* @param parent The parent of n
*/
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (n == scope.getRootNode()) return;
if (n.isParamList() && parent == scope.getRootNode()) {
handleFunctionInputs(parent);
return;
}
super.visit(t, n, parent);
}
/** Handle bleeding functions and function parameters. */
private void handleFunctionInputs(Node fnNode) {
// Handle bleeding functions.
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>
Node fnNameNode = fnNode.getFirstChild();
String fnName = fnNameNode.getString();
if (!fnName.isEmpty()) {
Scope.Var fnVar = scope.getVar(fnName);
if (fnVar == null ||
// Make sure we're not touching a native function. Native
// functions aren't bleeding, but may not have a declaration
// node.
(fnVar.getNameNode() != null &&
// Make sure that the function is actually bleeding by checking
// if has already been declared.
fnVar.getInitialValue() != fnNode)) {
defineSlot(fnNameNode, fnNode, fnNode.getJSType(), false);
}
}
declareArguments(fnNode);
}
/**
* Declares all of a function's arguments.
*/
private void declareArguments(Node functionNode) {
Node astParameters = functionNode.getFirstChild().getNext();
Node body = astParameters.getNext();
FunctionType functionType =
JSType.toMaybeFunctionType(functionNode.getJSType());
if (functionType != null) {
Node jsDocParameters = functionType.getParametersNode();
if (jsDocParameters != null) {
Node jsDocParameter = jsDocParameters.getFirstChild();
for (Node astParameter : astParameters.children()) {
if (jsDocParameter != null) {
defineSlot(astParameter, functionNode,
jsDocParameter.getJSType(), false);
jsDocParameter = jsDocParameter.getNext();
} else {
defineSlot(astParameter, functionNode, null, true);
}
}
}
}
} // end declareArguments
} // end LocalScopeBuilder
/**
* Does a first-order function analysis that just looks at simple things
* like what variables are escaped, and whether 'this' is used.
*/
private static class FirstOrderFunctionAnalyzer
extends AbstractScopedCallback implements CompilerPass {
private final AbstractCompiler compiler;
private final Map<Node, AstFunctionContents> data;
FirstOrderFunctionAnalyzer(
AbstractCompiler compiler, Map<Node, AstFunctionContents> outParam) {
this.compiler = compiler;
this.data = outParam;
}
@Override public void process(Node externs, Node root) {
if (externs == null)
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS> {
NodeTraversal.traverse(compiler, root, this);
} else {
NodeTraversal.traverseRoots(
compiler, ImmutableList.of(externs, root), this);
}
}
@Override public void enterScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
data.put(n, new AstFunctionContents(n));
}
}
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (t.inGlobalScope()) {
return;
}
if (n.isReturn() && n.getFirstChild() != null) {
data.get(t.getScopeRoot()).recordNonEmptyReturn();
} else if (n.isName() && NodeUtil.isLValue(n)) {
String name = n.getString();
Scope scope = t.getScope();
Var var = scope.getVar(name);
if (var != null) {
Scope ownerScope = var.getScope();
if (scope != ownerScope && ownerScope.isLocal()) {
data.get(ownerScope.getRootNode()).recordEscapedVarName(name);
}
}
}
}
}
private AstFunctionContents getFunctionAnalysisResults(@Nullable Node n) {
if (n == null) {
return null;
}
// Sometimes this will return null in things like
// NameReferenceGraphConstruction that build partial scopes.
return functionAnalysisResults.get(n);
}
}
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.javascript.jscomp.graph.GraphvizGraph;
import com.google.javascript.jscomp.graph.LinkedDirectedGraph;
import com.google.javascript.rhino.Node;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Pass factories and meta-data for native Compiler passes.
*
* @author nicksantos@google.com (Nick Santos)
*/
public abstract class PassConfig {
// Used by subclasses in this package.
final CompilerOptions options;
/**
* A memoized version of scopeCreator. It must be memoized so that
* we can make two separate passes over the AST, one for inferring types
* and one for checking types.
*/
private MemoizedScopeCreator typedScopeCreator;
/**
* This is the scope creator that {@code TypedScopeCreator} delegates to.
*/
private TypedScopeCreator internalScopeCreator;
/** The global typed scope. */
Scope topScope = null;
public PassConfig(CompilerOptions options) {
this.options = options;
}
/**
* Regenerates the top scope from scratch.
*
* @param compiler The compiler for which the global scope is regenerated.
* @param root The root of the AST.
*/
void regenerateGlobalTypedScope(AbstractCompiler compiler, Node root) {
internalScopeCreator = new TypedScope
Closure, 163
<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB>
} else {
NameInfo nameInfo = new NameInfo(name);
map.put(name, nameInfo);
symbolGraph.createNode(nameInfo);
return nameInfo;
}
}
private class ProcessProperties implements NodeTraversal.ScopedCallback {
// There are two types of context information on this stack:
// 1) Every scope has a NameContext corresponding to its scope.
// Variables are given VAR contexts.
// Prototype properties are given PROPERTY contexts.
// The global scope is given the special [global] context.
// And function expressions that we aren't able to give a reasonable
// name are given a special [anonymous] context.
// 2) Every assignment of a prototype property of a non-function is
// given a name context. These contexts do not have scopes.
private Stack<NameContext> symbolStack = new Stack<NameContext>();
<CHANGES>
private ProcessProperties() {
symbolStack.push(new NameContext(globalNode));
}
<CHANGEE>
@Override
public void enterScope(NodeTraversal t) {
<CHANGES>
symbolStack.peek().scope = t.getScope();
<CHANGEE>
// NOTE(nicksantos): We use the same anonymous node for all
// functions that do not have reasonable names. I can't remember
// at the moment why we do this. I think it's because anonymous
// nodes can never have in-edges. They're just there as a placeholder
// for scope information, and do not matter in the edge propagation.
<CHANGES>
<CHANGEE>
}
@Override
public void exitScope(NodeTraversal t) {
<CHANGES>
<CHANGEE>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
// Process prototype assignments to non-functions.
<CHANGES>
if (isPrototypePropertyAssign(n)) {
symbolStack.push(new NameContext(getNameInfoForName(
n.getFirstChild().getLastChild().getString(), PROPERTY)));
} else if (isGlobalFunctionDeclaration(t, n)) {
String name = parent.isName() ?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(new NameContext(getNameInfoForName(name, VAR)));
} else if (n.isFunction()) {
symbolStack.push(new<SCANS>Creator(compiler);
typedScopeCreator = new MemoizedScopeCreator(internalScopeCreator);
topScope = typedScopeCreator.createScope(root, null);
}
/**
* Regenerates the top scope potentially only for a sub-tree of AST and then
* copies information for the old global scope.
*
* @param compiler The compiler for which the global scope is generated.
* @param scriptRoot The root of the AST used to generate global scope.
*/
void patchGlobalTypedScope(AbstractCompiler compiler, Node scriptRoot) {
Preconditions.checkNotNull(internalScopeCreator);
internalScopeCreator.patchGlobalScope(topScope, scriptRoot);
}
/**
* Gets the scope creator for typed scopes.
*/
MemoizedScopeCreator getTypedScopeCreator() {
return typedScopeCreator;
}
/**
* Gets the global scope, with type information.
*/
Scope getTopScope() {
return topScope;
}
/**
* Gets the checking passes to run.
*
* Checking passes revolve around emitting warnings and errors.
* They also may include pre-processor passes needed to do
* error analysis more effectively.
*
* Clients that only want to analyze code (like IDEs) and not emit
* code will only run checks and not optimizations.
*/
abstract protected List<PassFactory> getChecks();
/**
* Gets the optimization passes to run.
*
* Optimization passes revolve around producing smaller and faster code.
* They should always run after checking passes.
*/
abstract protected List<PassFactory> getOptimizations();
/**
* Gets a graph of the passes run. For debugging.
*/
GraphvizGraph getPassGraph() {
LinkedDirectedGraph<String, String> graph =
LinkedDirectedGraph.createWithoutAnnotations();
Iterable<PassFactory> allPasses =
Iterables.concat(getChecks(), getOptimizations());
String lastPass = null;
String loopStart = null;
for (PassFactory pass : allPasses) {
String passName = pass.getName();
int i = 1;
while (graph.hasNode(passName)) {
passName = pass.getName() + (i++);
}
graph.createNode(passName);
if (loopStart == null && !pass.isOneTime